From 351702bd6304799600a342628f75c9f3264942ee Mon Sep 17 00:00:00 2001
From: Lakshitha Attanayaka
Date: Sat, 2 Jul 2022 11:01:17 +0530
Subject: [PATCH 1/5] implement identity server
---
IdentityServer/Configuration/Clients.cs | 47 +
IdentityServer/Configuration/Resources.cs | 38 +
IdentityServer/Configuration/Scopes.cs | 16 +
IdentityServer/Configuration/Users.cs | 28 +
.../Controllers/Account/AccountController.cs | 368 +
.../Controllers/Account/AccountOptions.cs | 20 +
.../Controllers/Account/ExternalController.cs | 196 +
.../Controllers/Account/ExternalProvider.cs | 12 +
.../Controllers/Account/LoggedOutViewModel.cs | 19 +
.../Controllers/Account/LoginInputModel.cs | 18 +
.../Controllers/Account/LoginViewModel.cs | 22 +
.../Controllers/Account/LogoutInputModel.cs | 11 +
.../Controllers/Account/LogoutViewModel.cs | 11 +
.../Controllers/Account/RedirectViewModel.cs | 12 +
.../Controllers/Consent/ConsentController.cs | 262 +
.../Controllers/Consent/ConsentInputModel.cs | 17 +
.../Controllers/Consent/ConsentOptions.cs | 16 +
.../Controllers/Consent/ConsentViewModel.cs | 19 +
.../Consent/ProcessConsentResult.cs | 21 +
.../Controllers/Consent/ScopeViewModel.cs | 16 +
.../Device/DeviceAuthorizationInputModel.cs | 11 +
.../Device/DeviceAuthorizationViewModel.cs | 12 +
.../Controllers/Device/DeviceController.cs | 232 +
.../Diagnostics/DiagnosticsController.cs | 29 +
.../Diagnostics/DiagnosticsViewModel.cs | 32 +
IdentityServer/Controllers/Extensions.cs | 27 +
.../Controllers/Grants/GrantsController.cs | 97 +
.../Controllers/Grants/GrantsViewModel.cs | 27 +
.../Controllers/Home/ErrorViewModel.cs | 22 +
.../Controllers/Home/HomeController.cs | 65 +
.../Controllers/SecurityHeadersAttribute.cs | 56 +
IdentityServer/Controllers/TestUsers.cs | 66 +
IdentityServer/IdentityServer.csproj | 7 +-
IdentityServer/Program.cs | 25 +-
.../Views/Account/AccessDenied.cshtml | 7 +
IdentityServer/Views/Account/LoggedOut.cshtml | 34 +
IdentityServer/Views/Account/Login.cshtml | 87 +
IdentityServer/Views/Account/Logout.cshtml | 15 +
IdentityServer/Views/Consent/Index.cshtml | 104 +
IdentityServer/Views/Device/Success.cshtml | 7 +
.../Views/Device/UserCodeCapture.cshtml | 23 +
.../Views/Device/UserCodeConfirmation.cshtml | 108 +
IdentityServer/Views/Diagnostics/Index.cshtml | 64 +
IdentityServer/Views/Grants/Index.cshtml | 87 +
IdentityServer/Views/Home/Index.cshtml | 32 +
IdentityServer/Views/Shared/Error.cshtml | 40 +
IdentityServer/Views/Shared/Redirect.cshtml | 11 +
IdentityServer/Views/Shared/_Layout.cshtml | 28 +
IdentityServer/Views/Shared/_Nav.cshtml | 33 +
.../Views/Shared/_ScopeListItem.cshtml | 34 +
.../Views/Shared/_ValidationSummary.cshtml | 7 +
IdentityServer/Views/_ViewImports.cshtml | 2 +
IdentityServer/Views/_ViewStart.cshtml | 3 +
IdentityServer/tempkey.jwk | 1 +
IdentityServer/wwwroot/css/site.css | 24 +
IdentityServer/wwwroot/css/site.min.css | 1 +
IdentityServer/wwwroot/css/site.scss | 42 +
IdentityServer/wwwroot/favicon.ico | Bin 0 -> 1150 bytes
IdentityServer/wwwroot/icon.jpg | Bin 0 -> 19482 bytes
IdentityServer/wwwroot/icon.png | Bin 0 -> 20796 bytes
IdentityServer/wwwroot/js/signin-redirect.js | 1 +
IdentityServer/wwwroot/js/signout-redirect.js | 6 +
IdentityServer/wwwroot/lib/bootstrap/LICENSE | 22 +
.../wwwroot/lib/bootstrap/README.md | 209 +
.../lib/bootstrap/dist/css/bootstrap-grid.css | 3899 ++++++
.../bootstrap/dist/css/bootstrap-grid.css.map | 1 +
.../bootstrap/dist/css/bootstrap-grid.min.css | 7 +
.../dist/css/bootstrap-grid.min.css.map | 1 +
.../bootstrap/dist/css/bootstrap-reboot.css | 327 +
.../dist/css/bootstrap-reboot.css.map | 1 +
.../dist/css/bootstrap-reboot.min.css | 8 +
.../dist/css/bootstrap-reboot.min.css.map | 1 +
.../lib/bootstrap/dist/css/bootstrap.css | 10224 +++++++++++++++
.../lib/bootstrap/dist/css/bootstrap.css.map | 1 +
.../lib/bootstrap/dist/css/bootstrap.min.css | 7 +
.../bootstrap/dist/css/bootstrap.min.css.map | 1 +
.../lib/bootstrap/dist/js/bootstrap.bundle.js | 7134 ++++++++++
.../bootstrap/dist/js/bootstrap.bundle.js.map | 1 +
.../bootstrap/dist/js/bootstrap.bundle.min.js | 7 +
.../dist/js/bootstrap.bundle.min.js.map | 1 +
.../lib/bootstrap/dist/js/bootstrap.js | 4521 +++++++
.../lib/bootstrap/dist/js/bootstrap.js.map | 1 +
.../lib/bootstrap/dist/js/bootstrap.min.js | 7 +
.../bootstrap/dist/js/bootstrap.min.js.map | 1 +
.../wwwroot/lib/bootstrap/scss/_alert.scss | 51 +
.../wwwroot/lib/bootstrap/scss/_badge.scss | 54 +
.../lib/bootstrap/scss/_breadcrumb.scss | 42 +
.../lib/bootstrap/scss/_button-group.scss | 163 +
.../wwwroot/lib/bootstrap/scss/_buttons.scss | 139 +
.../wwwroot/lib/bootstrap/scss/_card.scss | 278 +
.../wwwroot/lib/bootstrap/scss/_carousel.scss | 197 +
.../wwwroot/lib/bootstrap/scss/_close.scss | 41 +
.../wwwroot/lib/bootstrap/scss/_code.scss | 48 +
.../lib/bootstrap/scss/_custom-forms.scss | 521 +
.../wwwroot/lib/bootstrap/scss/_dropdown.scss | 191 +
.../wwwroot/lib/bootstrap/scss/_forms.scss | 338 +
.../lib/bootstrap/scss/_functions.scss | 134 +
.../wwwroot/lib/bootstrap/scss/_grid.scss | 69 +
.../wwwroot/lib/bootstrap/scss/_images.scss | 42 +
.../lib/bootstrap/scss/_input-group.scss | 191 +
.../lib/bootstrap/scss/_jumbotron.scss | 17 +
.../lib/bootstrap/scss/_list-group.scss | 158 +
.../wwwroot/lib/bootstrap/scss/_media.scss | 8 +
.../wwwroot/lib/bootstrap/scss/_mixins.scss | 47 +
.../wwwroot/lib/bootstrap/scss/_modal.scss | 239 +
.../wwwroot/lib/bootstrap/scss/_nav.scss | 120 +
.../wwwroot/lib/bootstrap/scss/_navbar.scss | 324 +
.../lib/bootstrap/scss/_pagination.scss | 73 +
.../wwwroot/lib/bootstrap/scss/_popover.scss | 170 +
.../wwwroot/lib/bootstrap/scss/_print.scss | 141 +
.../wwwroot/lib/bootstrap/scss/_progress.scss | 46 +
.../wwwroot/lib/bootstrap/scss/_reboot.scss | 482 +
.../wwwroot/lib/bootstrap/scss/_root.scss | 20 +
.../wwwroot/lib/bootstrap/scss/_spinners.scss | 55 +
.../wwwroot/lib/bootstrap/scss/_tables.scss | 185 +
.../wwwroot/lib/bootstrap/scss/_toasts.scss | 44 +
.../wwwroot/lib/bootstrap/scss/_tooltip.scss | 115 +
.../lib/bootstrap/scss/_transitions.scss | 20 +
.../wwwroot/lib/bootstrap/scss/_type.scss | 125 +
.../lib/bootstrap/scss/_utilities.scss | 17 +
.../lib/bootstrap/scss/_variables.scss | 1143 ++
.../lib/bootstrap/scss/bootstrap-grid.scss | 29 +
.../lib/bootstrap/scss/bootstrap-reboot.scss | 12 +
.../wwwroot/lib/bootstrap/scss/bootstrap.scss | 44 +
.../lib/bootstrap/scss/mixins/_alert.scss | 13 +
.../scss/mixins/_background-variant.scss | 22 +
.../lib/bootstrap/scss/mixins/_badge.scss | 17 +
.../bootstrap/scss/mixins/_border-radius.scss | 63 +
.../bootstrap/scss/mixins/_box-shadow.scss | 20 +
.../bootstrap/scss/mixins/_breakpoints.scss | 123 +
.../lib/bootstrap/scss/mixins/_buttons.scss | 110 +
.../lib/bootstrap/scss/mixins/_caret.scss | 62 +
.../lib/bootstrap/scss/mixins/_clearfix.scss | 7 +
.../lib/bootstrap/scss/mixins/_deprecate.scss | 10 +
.../lib/bootstrap/scss/mixins/_float.scss | 14 +
.../lib/bootstrap/scss/mixins/_forms.scss | 177 +
.../lib/bootstrap/scss/mixins/_gradients.scss | 45 +
.../scss/mixins/_grid-framework.scss | 71 +
.../lib/bootstrap/scss/mixins/_grid.scss | 69 +
.../lib/bootstrap/scss/mixins/_hover.scss | 37 +
.../lib/bootstrap/scss/mixins/_image.scss | 36 +
.../bootstrap/scss/mixins/_list-group.scss | 21 +
.../lib/bootstrap/scss/mixins/_lists.scss | 7 +
.../bootstrap/scss/mixins/_nav-divider.scss | 11 +
.../bootstrap/scss/mixins/_pagination.scss | 22 +
.../bootstrap/scss/mixins/_reset-text.scss | 17 +
.../lib/bootstrap/scss/mixins/_resize.scss | 6 +
.../bootstrap/scss/mixins/_screen-reader.scss | 34 +
.../lib/bootstrap/scss/mixins/_size.scss | 7 +
.../lib/bootstrap/scss/mixins/_table-row.scss | 39 +
.../bootstrap/scss/mixins/_text-emphasis.scss | 17 +
.../lib/bootstrap/scss/mixins/_text-hide.scss | 11 +
.../bootstrap/scss/mixins/_text-truncate.scss | 8 +
.../bootstrap/scss/mixins/_transition.scss | 16 +
.../bootstrap/scss/mixins/_visibility.scss | 8 +
.../lib/bootstrap/scss/utilities/_align.scss | 8 +
.../bootstrap/scss/utilities/_background.scss | 19 +
.../bootstrap/scss/utilities/_borders.scss | 75 +
.../bootstrap/scss/utilities/_clearfix.scss | 3 +
.../bootstrap/scss/utilities/_display.scss | 26 +
.../lib/bootstrap/scss/utilities/_embed.scss | 39 +
.../lib/bootstrap/scss/utilities/_flex.scss | 51 +
.../lib/bootstrap/scss/utilities/_float.scss | 11 +
.../bootstrap/scss/utilities/_overflow.scss | 5 +
.../bootstrap/scss/utilities/_position.scss | 32 +
.../scss/utilities/_screenreaders.scss | 11 +
.../bootstrap/scss/utilities/_shadows.scss | 6 +
.../lib/bootstrap/scss/utilities/_sizing.scss | 20 +
.../bootstrap/scss/utilities/_spacing.scss | 73 +
.../scss/utilities/_stretched-link.scss | 19 +
.../lib/bootstrap/scss/utilities/_text.scss | 72 +
.../bootstrap/scss/utilities/_visibility.scss | 13 +
.../lib/bootstrap/scss/vendor/_rfs.scss | 204 +
IdentityServer/wwwroot/lib/jquery/LICENSE.txt | 20 +
IdentityServer/wwwroot/lib/jquery/README.md | 62 +
.../wwwroot/lib/jquery/dist/jquery.js | 10872 ++++++++++++++++
.../wwwroot/lib/jquery/dist/jquery.min.js | 2 +
.../wwwroot/lib/jquery/dist/jquery.min.map | 1 +
.../wwwroot/lib/jquery/dist/jquery.slim.js | 8777 +++++++++++++
.../lib/jquery/dist/jquery.slim.min.js | 2 +
.../lib/jquery/dist/jquery.slim.min.map | 1 +
181 files changed, 56730 insertions(+), 7 deletions(-)
create mode 100644 IdentityServer/Configuration/Clients.cs
create mode 100644 IdentityServer/Configuration/Resources.cs
create mode 100644 IdentityServer/Configuration/Scopes.cs
create mode 100644 IdentityServer/Configuration/Users.cs
create mode 100644 IdentityServer/Controllers/Account/AccountController.cs
create mode 100644 IdentityServer/Controllers/Account/AccountOptions.cs
create mode 100644 IdentityServer/Controllers/Account/ExternalController.cs
create mode 100644 IdentityServer/Controllers/Account/ExternalProvider.cs
create mode 100644 IdentityServer/Controllers/Account/LoggedOutViewModel.cs
create mode 100644 IdentityServer/Controllers/Account/LoginInputModel.cs
create mode 100644 IdentityServer/Controllers/Account/LoginViewModel.cs
create mode 100644 IdentityServer/Controllers/Account/LogoutInputModel.cs
create mode 100644 IdentityServer/Controllers/Account/LogoutViewModel.cs
create mode 100644 IdentityServer/Controllers/Account/RedirectViewModel.cs
create mode 100644 IdentityServer/Controllers/Consent/ConsentController.cs
create mode 100644 IdentityServer/Controllers/Consent/ConsentInputModel.cs
create mode 100644 IdentityServer/Controllers/Consent/ConsentOptions.cs
create mode 100644 IdentityServer/Controllers/Consent/ConsentViewModel.cs
create mode 100644 IdentityServer/Controllers/Consent/ProcessConsentResult.cs
create mode 100644 IdentityServer/Controllers/Consent/ScopeViewModel.cs
create mode 100644 IdentityServer/Controllers/Device/DeviceAuthorizationInputModel.cs
create mode 100644 IdentityServer/Controllers/Device/DeviceAuthorizationViewModel.cs
create mode 100644 IdentityServer/Controllers/Device/DeviceController.cs
create mode 100644 IdentityServer/Controllers/Diagnostics/DiagnosticsController.cs
create mode 100644 IdentityServer/Controllers/Diagnostics/DiagnosticsViewModel.cs
create mode 100644 IdentityServer/Controllers/Extensions.cs
create mode 100644 IdentityServer/Controllers/Grants/GrantsController.cs
create mode 100644 IdentityServer/Controllers/Grants/GrantsViewModel.cs
create mode 100644 IdentityServer/Controllers/Home/ErrorViewModel.cs
create mode 100644 IdentityServer/Controllers/Home/HomeController.cs
create mode 100644 IdentityServer/Controllers/SecurityHeadersAttribute.cs
create mode 100644 IdentityServer/Controllers/TestUsers.cs
create mode 100644 IdentityServer/Views/Account/AccessDenied.cshtml
create mode 100644 IdentityServer/Views/Account/LoggedOut.cshtml
create mode 100644 IdentityServer/Views/Account/Login.cshtml
create mode 100644 IdentityServer/Views/Account/Logout.cshtml
create mode 100644 IdentityServer/Views/Consent/Index.cshtml
create mode 100644 IdentityServer/Views/Device/Success.cshtml
create mode 100644 IdentityServer/Views/Device/UserCodeCapture.cshtml
create mode 100644 IdentityServer/Views/Device/UserCodeConfirmation.cshtml
create mode 100644 IdentityServer/Views/Diagnostics/Index.cshtml
create mode 100644 IdentityServer/Views/Grants/Index.cshtml
create mode 100644 IdentityServer/Views/Home/Index.cshtml
create mode 100644 IdentityServer/Views/Shared/Error.cshtml
create mode 100644 IdentityServer/Views/Shared/Redirect.cshtml
create mode 100644 IdentityServer/Views/Shared/_Layout.cshtml
create mode 100644 IdentityServer/Views/Shared/_Nav.cshtml
create mode 100644 IdentityServer/Views/Shared/_ScopeListItem.cshtml
create mode 100644 IdentityServer/Views/Shared/_ValidationSummary.cshtml
create mode 100644 IdentityServer/Views/_ViewImports.cshtml
create mode 100644 IdentityServer/Views/_ViewStart.cshtml
create mode 100644 IdentityServer/tempkey.jwk
create mode 100644 IdentityServer/wwwroot/css/site.css
create mode 100644 IdentityServer/wwwroot/css/site.min.css
create mode 100644 IdentityServer/wwwroot/css/site.scss
create mode 100644 IdentityServer/wwwroot/favicon.ico
create mode 100644 IdentityServer/wwwroot/icon.jpg
create mode 100644 IdentityServer/wwwroot/icon.png
create mode 100644 IdentityServer/wwwroot/js/signin-redirect.js
create mode 100644 IdentityServer/wwwroot/js/signout-redirect.js
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/LICENSE
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/README.md
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap.css
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/js/bootstrap.js
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_alert.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_badge.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_breadcrumb.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_button-group.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_buttons.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_card.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_carousel.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_close.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_code.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_custom-forms.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_dropdown.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_forms.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_functions.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_grid.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_images.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_input-group.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_jumbotron.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_list-group.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_media.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_mixins.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_modal.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_nav.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_navbar.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_pagination.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_popover.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_print.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_progress.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_reboot.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_root.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_spinners.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_tables.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_toasts.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_tooltip.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_transitions.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_type.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_utilities.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/_variables.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/bootstrap-grid.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/bootstrap-reboot.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/bootstrap.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_alert.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_background-variant.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_badge.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_border-radius.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_box-shadow.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_breakpoints.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_buttons.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_caret.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_clearfix.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_deprecate.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_float.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_forms.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_gradients.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_grid-framework.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_grid.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_hover.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_image.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_list-group.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_lists.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_nav-divider.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_pagination.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_reset-text.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_resize.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_screen-reader.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_size.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_table-row.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_text-emphasis.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_text-hide.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_text-truncate.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_transition.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/mixins/_visibility.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_align.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_background.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_borders.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_clearfix.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_display.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_embed.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_flex.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_float.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_overflow.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_position.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_screenreaders.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_shadows.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_sizing.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_spacing.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_stretched-link.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_text.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/utilities/_visibility.scss
create mode 100644 IdentityServer/wwwroot/lib/bootstrap/scss/vendor/_rfs.scss
create mode 100644 IdentityServer/wwwroot/lib/jquery/LICENSE.txt
create mode 100644 IdentityServer/wwwroot/lib/jquery/README.md
create mode 100644 IdentityServer/wwwroot/lib/jquery/dist/jquery.js
create mode 100644 IdentityServer/wwwroot/lib/jquery/dist/jquery.min.js
create mode 100644 IdentityServer/wwwroot/lib/jquery/dist/jquery.min.map
create mode 100644 IdentityServer/wwwroot/lib/jquery/dist/jquery.slim.js
create mode 100644 IdentityServer/wwwroot/lib/jquery/dist/jquery.slim.min.js
create mode 100644 IdentityServer/wwwroot/lib/jquery/dist/jquery.slim.min.map
diff --git a/IdentityServer/Configuration/Clients.cs b/IdentityServer/Configuration/Clients.cs
new file mode 100644
index 0000000..668d9e5
--- /dev/null
+++ b/IdentityServer/Configuration/Clients.cs
@@ -0,0 +1,47 @@
+using IdentityServer4;
+using IdentityServer4.Models;
+
+namespace IdentityServer.Configuration
+{
+ public class Clients
+ {
+ public static IEnumerable Get()
+ {
+ return new List
+ {
+ new Client
+ {
+ ClientId = "onShoppingApi",
+ ClientName = "ASP.NET Core OnShopping Api",
+ AllowedGrantTypes = GrantTypes.ClientCredentials,
+ ClientSecrets = new List {new Secret("OnPlatform".Sha256())},
+ AllowedScopes = new List { "onShoppingApi.read" }
+ },
+ new Client
+ {
+ ClientId = "onShoppingAuth",
+ ClientName = "OnShopping Mobile App",
+ ClientSecrets = new List {new Secret("OnPlatform".Sha256())},
+
+ AllowedGrantTypes = GrantTypes.Code,
+ RedirectUris = new List {"onshopping:/authenticated"},
+ AllowedScopes = new List
+ {
+ IdentityServerConstants.StandardScopes.OpenId,
+ IdentityServerConstants.StandardScopes.Profile,
+ IdentityServerConstants.StandardScopes.Email,
+ "role",
+ "onShoppingApi.read"
+ },
+ PostLogoutRedirectUris = new List
+ {
+ "onshopping:/signout-callback-oidc",
+ },
+
+ RequirePkce = true,
+ AllowPlainTextPkce = false
+ }
+ };
+ }
+ }
+}
diff --git a/IdentityServer/Configuration/Resources.cs b/IdentityServer/Configuration/Resources.cs
new file mode 100644
index 0000000..b120939
--- /dev/null
+++ b/IdentityServer/Configuration/Resources.cs
@@ -0,0 +1,38 @@
+using IdentityServer4.Models;
+
+namespace IdentityServer.Configuration
+{
+ public class Resources
+ {
+ public static IEnumerable GetIdentityResources()
+ {
+ return new[]
+ {
+ new IdentityResources.OpenId(),
+ new IdentityResources.Profile(),
+ new IdentityResources.Email(),
+ new IdentityResource
+ {
+ Name = "role",
+ UserClaims = new List {"role"}
+ }
+ };
+ }
+
+ public static IEnumerable GetApiResources()
+ {
+ return new[]
+ {
+ new ApiResource
+ {
+ Name = "onShoppingApi",
+ DisplayName = "OnShopping Api",
+ Description = "Allow the application to access OnShopping Api on your behalf",
+ Scopes = new List { "onShoppingApi.read", "onShoppingApi.write"},
+ ApiSecrets = new List {new Secret("OnPlatform".Sha256())},
+ UserClaims = new List {"role"}
+ }
+ };
+ }
+ }
+}
diff --git a/IdentityServer/Configuration/Scopes.cs b/IdentityServer/Configuration/Scopes.cs
new file mode 100644
index 0000000..516cb80
--- /dev/null
+++ b/IdentityServer/Configuration/Scopes.cs
@@ -0,0 +1,16 @@
+using IdentityServer4.Models;
+
+namespace IdentityServer.Configuration
+{
+ public class Scopes
+ {
+ public static IEnumerable GetApiScopes()
+ {
+ return new[]
+ {
+ new ApiScope("onShoppingApi.read", "Read Access to OnShopping Api"),
+ new ApiScope("onShoppingApi.write", "Write Access to OnShopping Api"),
+ };
+ }
+ }
+}
diff --git a/IdentityServer/Configuration/Users.cs b/IdentityServer/Configuration/Users.cs
new file mode 100644
index 0000000..3e5571e
--- /dev/null
+++ b/IdentityServer/Configuration/Users.cs
@@ -0,0 +1,28 @@
+using IdentityModel;
+using IdentityServer4.Test;
+using System.Security.Claims;
+
+namespace IdentityServer.Configuration
+{
+ public class Users
+ {
+ public static List Get()
+ {
+ return new List
+ {
+ new TestUser
+ {
+ SubjectId = "56892347",
+ Username = "procoder",
+ Password = "password",
+ Claims = new List
+ {
+ new Claim(JwtClaimTypes.Email, "support@procodeguide.com"),
+ new Claim(JwtClaimTypes.Role, "admin"),
+ new Claim(JwtClaimTypes.WebSite, "https://procodeguide.com")
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/IdentityServer/Controllers/Account/AccountController.cs b/IdentityServer/Controllers/Account/AccountController.cs
new file mode 100644
index 0000000..dd0c059
--- /dev/null
+++ b/IdentityServer/Controllers/Account/AccountController.cs
@@ -0,0 +1,368 @@
+// 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.
+
+
+using IdentityModel;
+using IdentityServer4;
+using IdentityServer4.Events;
+using IdentityServer4.Extensions;
+using IdentityServer4.Models;
+using IdentityServer4.Services;
+using IdentityServer4.Stores;
+using IdentityServer4.Test;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace IdentityServer.Controllers.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 RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl });
+ }
+
+ return View(vm);
+ }
+
+ ///
+ /// Handle postback from username/password login
+ ///
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public async Task Login(LoginInputModel model, string button)
+ {
+ // check if we are in the context of an authorization request
+ var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
+
+ // the user clicked the "cancel" button
+ if (button != "login")
+ {
+ 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 this.LoadingPage("Redirect", model.ReturnUrl);
+ }
+
+ return Redirect(model.ReturnUrl);
+ }
+ else
+ {
+ // since we don't have a valid context, then we just go back to the home page
+ return Redirect("~/");
+ }
+ }
+
+ 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 this.LoadingPage("Redirect", model.ReturnUrl);
+ }
+
+ // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
+ return Redirect(model.ReturnUrl);
+ }
+
+ // request for a local page
+ if (Url.IsLocalUrl(model.ReturnUrl))
+ {
+ return Redirect(model.ReturnUrl);
+ }
+ 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);
+ }
+
+
+ ///
+ /// 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 == IdentityServer4.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 != IdentityServer4.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;
+ }
+ }
+}
diff --git a/IdentityServer/Controllers/Account/AccountOptions.cs b/IdentityServer/Controllers/Account/AccountOptions.cs
new file mode 100644
index 0000000..bd26eb3
--- /dev/null
+++ b/IdentityServer/Controllers/Account/AccountOptions.cs
@@ -0,0 +1,20 @@
+// 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.
+
+
+using System;
+
+namespace IdentityServer.Controllers.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";
+ }
+}
diff --git a/IdentityServer/Controllers/Account/ExternalController.cs b/IdentityServer/Controllers/Account/ExternalController.cs
new file mode 100644
index 0000000..66c402a
--- /dev/null
+++ b/IdentityServer/Controllers/Account/ExternalController.cs
@@ -0,0 +1,196 @@
+using IdentityModel;
+using IdentityServer4;
+using IdentityServer4.Events;
+using IdentityServer4.Services;
+using IdentityServer4.Stores;
+using IdentityServer4.Test;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Threading.Tasks;
+
+namespace IdentityServer.Controllers.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 } });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Account/ExternalProvider.cs b/IdentityServer/Controllers/Account/ExternalProvider.cs
new file mode 100644
index 0000000..31f4368
--- /dev/null
+++ b/IdentityServer/Controllers/Account/ExternalProvider.cs
@@ -0,0 +1,12 @@
+// 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.
+
+
+namespace IdentityServer.Controllers.UI
+{
+ public class ExternalProvider
+ {
+ public string DisplayName { get; set; }
+ public string AuthenticationScheme { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Account/LoggedOutViewModel.cs b/IdentityServer/Controllers/Account/LoggedOutViewModel.cs
new file mode 100644
index 0000000..17624de
--- /dev/null
+++ b/IdentityServer/Controllers/Account/LoggedOutViewModel.cs
@@ -0,0 +1,19 @@
+// 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.
+
+
+namespace IdentityServer.Controllers.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; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Account/LoginInputModel.cs b/IdentityServer/Controllers/Account/LoginInputModel.cs
new file mode 100644
index 0000000..1caffe3
--- /dev/null
+++ b/IdentityServer/Controllers/Account/LoginInputModel.cs
@@ -0,0 +1,18 @@
+// 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.
+
+
+using System.ComponentModel.DataAnnotations;
+
+namespace IdentityServer.Controllers.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; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Account/LoginViewModel.cs b/IdentityServer/Controllers/Account/LoginViewModel.cs
new file mode 100644
index 0000000..7cfabe4
--- /dev/null
+++ b/IdentityServer/Controllers/Account/LoginViewModel.cs
@@ -0,0 +1,22 @@
+// 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.
+
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace IdentityServer.Controllers.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;
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Account/LogoutInputModel.cs b/IdentityServer/Controllers/Account/LogoutInputModel.cs
new file mode 100644
index 0000000..13f91eb
--- /dev/null
+++ b/IdentityServer/Controllers/Account/LogoutInputModel.cs
@@ -0,0 +1,11 @@
+// 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.
+
+
+namespace IdentityServer.Controllers.UI
+{
+ public class LogoutInputModel
+ {
+ public string LogoutId { get; set; }
+ }
+}
diff --git a/IdentityServer/Controllers/Account/LogoutViewModel.cs b/IdentityServer/Controllers/Account/LogoutViewModel.cs
new file mode 100644
index 0000000..befb68c
--- /dev/null
+++ b/IdentityServer/Controllers/Account/LogoutViewModel.cs
@@ -0,0 +1,11 @@
+// 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.
+
+
+namespace IdentityServer.Controllers.UI
+{
+ public class LogoutViewModel : LogoutInputModel
+ {
+ public bool ShowLogoutPrompt { get; set; } = true;
+ }
+}
diff --git a/IdentityServer/Controllers/Account/RedirectViewModel.cs b/IdentityServer/Controllers/Account/RedirectViewModel.cs
new file mode 100644
index 0000000..6f8ed2a
--- /dev/null
+++ b/IdentityServer/Controllers/Account/RedirectViewModel.cs
@@ -0,0 +1,12 @@
+// 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.
+
+
+
+namespace IdentityServer.Controllers.UI
+{
+ public class RedirectViewModel
+ {
+ public string RedirectUrl { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Consent/ConsentController.cs b/IdentityServer/Controllers/Consent/ConsentController.cs
new file mode 100644
index 0000000..73c5f92
--- /dev/null
+++ b/IdentityServer/Controllers/Consent/ConsentController.cs
@@ -0,0 +1,262 @@
+// 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.
+
+
+using IdentityServer4.Events;
+using IdentityServer4.Models;
+using IdentityServer4.Services;
+using IdentityServer4.Extensions;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using System.Linq;
+using System.Threading.Tasks;
+using IdentityServer4.Validation;
+using System.Collections.Generic;
+using System;
+
+namespace IdentityServer.Controllers.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 Redirect(result.RedirectUri);
+ }
+
+ 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 != IdentityServer4.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);
+ }
+
+ 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(IdentityServer4.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 = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess,
+ DisplayName = ConsentOptions.OfflineAccessDisplayName,
+ Description = ConsentOptions.OfflineAccessDescription,
+ Emphasize = true,
+ Checked = check
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Consent/ConsentInputModel.cs b/IdentityServer/Controllers/Consent/ConsentInputModel.cs
new file mode 100644
index 0000000..56e38ca
--- /dev/null
+++ b/IdentityServer/Controllers/Consent/ConsentInputModel.cs
@@ -0,0 +1,17 @@
+// 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.
+
+
+using System.Collections.Generic;
+
+namespace IdentityServer.Controllers.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; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Consent/ConsentOptions.cs b/IdentityServer/Controllers/Consent/ConsentOptions.cs
new file mode 100644
index 0000000..9aea3c6
--- /dev/null
+++ b/IdentityServer/Controllers/Consent/ConsentOptions.cs
@@ -0,0 +1,16 @@
+// 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.
+
+
+namespace IdentityServer.Controllers.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";
+ }
+}
diff --git a/IdentityServer/Controllers/Consent/ConsentViewModel.cs b/IdentityServer/Controllers/Consent/ConsentViewModel.cs
new file mode 100644
index 0000000..991ccd9
--- /dev/null
+++ b/IdentityServer/Controllers/Consent/ConsentViewModel.cs
@@ -0,0 +1,19 @@
+// 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.
+
+
+using System.Collections.Generic;
+
+namespace IdentityServer.Controllers.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; }
+ }
+}
diff --git a/IdentityServer/Controllers/Consent/ProcessConsentResult.cs b/IdentityServer/Controllers/Consent/ProcessConsentResult.cs
new file mode 100644
index 0000000..5643ece
--- /dev/null
+++ b/IdentityServer/Controllers/Consent/ProcessConsentResult.cs
@@ -0,0 +1,21 @@
+// 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.
+
+
+using IdentityServer4.Models;
+
+namespace IdentityServer.Controllers.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; }
+ }
+}
diff --git a/IdentityServer/Controllers/Consent/ScopeViewModel.cs b/IdentityServer/Controllers/Consent/ScopeViewModel.cs
new file mode 100644
index 0000000..ca2c058
--- /dev/null
+++ b/IdentityServer/Controllers/Consent/ScopeViewModel.cs
@@ -0,0 +1,16 @@
+// 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.
+
+
+namespace IdentityServer.Controllers.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; }
+ }
+}
diff --git a/IdentityServer/Controllers/Device/DeviceAuthorizationInputModel.cs b/IdentityServer/Controllers/Device/DeviceAuthorizationInputModel.cs
new file mode 100644
index 0000000..8ed3b43
--- /dev/null
+++ b/IdentityServer/Controllers/Device/DeviceAuthorizationInputModel.cs
@@ -0,0 +1,11 @@
+// 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.
+
+
+namespace IdentityServer.Controllers.UI
+{
+ public class DeviceAuthorizationInputModel : ConsentInputModel
+ {
+ public string UserCode { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Device/DeviceAuthorizationViewModel.cs b/IdentityServer/Controllers/Device/DeviceAuthorizationViewModel.cs
new file mode 100644
index 0000000..6feae92
--- /dev/null
+++ b/IdentityServer/Controllers/Device/DeviceAuthorizationViewModel.cs
@@ -0,0 +1,12 @@
+// 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.
+
+
+namespace IdentityServer.Controllers.UI
+{
+ public class DeviceAuthorizationViewModel : ConsentViewModel
+ {
+ public string UserCode { get; set; }
+ public bool ConfirmUserCode { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Device/DeviceController.cs b/IdentityServer/Controllers/Device/DeviceController.cs
new file mode 100644
index 0000000..6eedde1
--- /dev/null
+++ b/IdentityServer/Controllers/Device/DeviceController.cs
@@ -0,0 +1,232 @@
+// 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.
+
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using IdentityServer4.Configuration;
+using IdentityServer4.Events;
+using IdentityServer4.Extensions;
+using IdentityServer4.Models;
+using IdentityServer4.Services;
+using IdentityServer4.Validation;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace IdentityServer.Controllers.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 != IdentityServer4.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(IdentityServer4.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 = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess,
+ DisplayName = ConsentOptions.OfflineAccessDisplayName,
+ Description = ConsentOptions.OfflineAccessDescription,
+ Emphasize = true,
+ Checked = check
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Diagnostics/DiagnosticsController.cs b/IdentityServer/Controllers/Diagnostics/DiagnosticsController.cs
new file mode 100644
index 0000000..f3c93e9
--- /dev/null
+++ b/IdentityServer/Controllers/Diagnostics/DiagnosticsController.cs
@@ -0,0 +1,29 @@
+// 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.
+
+
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace IdentityServer.Controllers.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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Diagnostics/DiagnosticsViewModel.cs b/IdentityServer/Controllers/Diagnostics/DiagnosticsViewModel.cs
new file mode 100644
index 0000000..7334dfa
--- /dev/null
+++ b/IdentityServer/Controllers/Diagnostics/DiagnosticsViewModel.cs
@@ -0,0 +1,32 @@
+// 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.
+
+
+using IdentityModel;
+using Microsoft.AspNetCore.Authentication;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IdentityServer.Controllers.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();
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Extensions.cs b/IdentityServer/Controllers/Extensions.cs
new file mode 100644
index 0000000..87aac8c
--- /dev/null
+++ b/IdentityServer/Controllers/Extensions.cs
@@ -0,0 +1,27 @@
+using System;
+using IdentityServer4.Models;
+using Microsoft.AspNetCore.Mvc;
+
+namespace IdentityServer.Controllers.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 });
+ }
+ }
+}
diff --git a/IdentityServer/Controllers/Grants/GrantsController.cs b/IdentityServer/Controllers/Grants/GrantsController.cs
new file mode 100644
index 0000000..740873c
--- /dev/null
+++ b/IdentityServer/Controllers/Grants/GrantsController.cs
@@ -0,0 +1,97 @@
+// 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.
+
+
+using IdentityServer4.Services;
+using IdentityServer4.Stores;
+using Microsoft.AspNetCore.Mvc;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using IdentityServer4.Events;
+using IdentityServer4.Extensions;
+
+namespace IdentityServer.Controllers.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
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Grants/GrantsViewModel.cs b/IdentityServer/Controllers/Grants/GrantsViewModel.cs
new file mode 100644
index 0000000..30b4c54
--- /dev/null
+++ b/IdentityServer/Controllers/Grants/GrantsViewModel.cs
@@ -0,0 +1,27 @@
+// 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.
+
+
+using System;
+using System.Collections.Generic;
+
+namespace IdentityServer.Controllers.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; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Home/ErrorViewModel.cs b/IdentityServer/Controllers/Home/ErrorViewModel.cs
new file mode 100644
index 0000000..9c9741c
--- /dev/null
+++ b/IdentityServer/Controllers/Home/ErrorViewModel.cs
@@ -0,0 +1,22 @@
+// 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.
+
+
+using IdentityServer4.Models;
+
+namespace IdentityServer.Controllers.UI
+{
+ public class ErrorViewModel
+ {
+ public ErrorViewModel()
+ {
+ }
+
+ public ErrorViewModel(string error)
+ {
+ Error = new ErrorMessage { Error = error };
+ }
+
+ public ErrorMessage Error { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/Home/HomeController.cs b/IdentityServer/Controllers/Home/HomeController.cs
new file mode 100644
index 0000000..83a163c
--- /dev/null
+++ b/IdentityServer/Controllers/Home/HomeController.cs
@@ -0,0 +1,65 @@
+// 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.
+
+
+using IdentityServer4.Services;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System.Threading.Tasks;
+
+namespace IdentityServer.Controllers.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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/Controllers/SecurityHeadersAttribute.cs b/IdentityServer/Controllers/SecurityHeadersAttribute.cs
new file mode 100644
index 0000000..d48502c
--- /dev/null
+++ b/IdentityServer/Controllers/SecurityHeadersAttribute.cs
@@ -0,0 +1,56 @@
+// 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.
+
+
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace IdentityServer.Controllers.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.Add("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.Add("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.Add("Content-Security-Policy", csp);
+ }
+ // and once again for IE
+ if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
+ {
+ context.HttpContext.Response.Headers.Add("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.Add("Referrer-Policy", referrer_policy);
+ }
+ }
+ }
+ }
+}
diff --git a/IdentityServer/Controllers/TestUsers.cs b/IdentityServer/Controllers/TestUsers.cs
new file mode 100644
index 0000000..e2de08d
--- /dev/null
+++ b/IdentityServer/Controllers/TestUsers.cs
@@ -0,0 +1,66 @@
+// 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.
+
+
+using IdentityModel;
+using IdentityServer4.Test;
+using System.Collections.Generic;
+using System.Security.Claims;
+using System.Text.Json;
+using IdentityServer4;
+
+namespace IdentityServer.Controllers.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)
+ }
+ }
+ };
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/IdentityServer/IdentityServer.csproj b/IdentityServer/IdentityServer.csproj
index 14554a8..70599dd 100644
--- a/IdentityServer/IdentityServer.csproj
+++ b/IdentityServer/IdentityServer.csproj
@@ -1,4 +1,4 @@
-
+net6.0
@@ -7,11 +7,8 @@
+
-
-
-
-
diff --git a/IdentityServer/Program.cs b/IdentityServer/Program.cs
index df2434c..b820a36 100644
--- a/IdentityServer/Program.cs
+++ b/IdentityServer/Program.cs
@@ -1,3 +1,5 @@
+using IdentityServer.Configuration;
+
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
@@ -7,17 +9,36 @@
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
+builder.Services.AddIdentityServer().AddInMemoryClients(Clients.Get())
+ .AddInMemoryIdentityResources(Resources.GetIdentityResources())
+ .AddInMemoryApiResources(Resources.GetApiResources())
+ .AddInMemoryApiScopes(Scopes.GetApiScopes())
+ .AddTestUsers(Users.Get())
+ .AddDeveloperSigningCredential();
+
+builder.Services.AddControllersWithViews();
+
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
- app.UseSwagger();
- app.UseSwaggerUI();
+ //TODO
+ app.UseDeveloperExceptionPage();
}
+app.UseStaticFiles();
+app.UseRouting();
app.UseAuthorization();
+app.UseIdentityServer();
+app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
+
+app.UseSwagger();
+app.UseSwaggerUI();
+
+//app.UseHttpsRedirection();
+
app.MapControllers();
app.Run();
diff --git a/IdentityServer/Views/Account/AccessDenied.cshtml b/IdentityServer/Views/Account/AccessDenied.cshtml
new file mode 100644
index 0000000..0745189
--- /dev/null
+++ b/IdentityServer/Views/Account/AccessDenied.cshtml
@@ -0,0 +1,7 @@
+
+
+
+
Access Denied
+
You do not have access to that resource.
+
+
\ No newline at end of file
diff --git a/IdentityServer/Views/Account/LoggedOut.cshtml b/IdentityServer/Views/Account/LoggedOut.cshtml
new file mode 100644
index 0000000..dc9fbf7
--- /dev/null
+++ b/IdentityServer/Views/Account/LoggedOut.cshtml
@@ -0,0 +1,34 @@
+@model LoggedOutViewModel
+
+@{
+ // set this so the layout rendering sees an anonymous user
+ ViewData["signed-out"] = true;
+}
+
+
+ Invalid login request
+ There are no login schemes configured for this request.
+
+ }
+
+
\ No newline at end of file
diff --git a/IdentityServer/Views/Account/Logout.cshtml b/IdentityServer/Views/Account/Logout.cshtml
new file mode 100644
index 0000000..b49dee0
--- /dev/null
+++ b/IdentityServer/Views/Account/Logout.cshtml
@@ -0,0 +1,15 @@
+@model LogoutViewModel
+
+
+ @foreach (var name in grant.IdentityGrantNames)
+ {
+
@name
+ }
+
+
+ }
+ @if (grant.ApiGrantNames.Any())
+ {
+
+
+
+ @foreach (var name in grant.ApiGrantNames)
+ {
+
@name
+ }
+
+
+ }
+
+
+ }
+ }
+
\ No newline at end of file
diff --git a/IdentityServer/Views/Home/Index.cshtml b/IdentityServer/Views/Home/Index.cshtml
new file mode 100644
index 0000000..36b2bfc
--- /dev/null
+++ b/IdentityServer/Views/Home/Index.cshtml
@@ -0,0 +1,32 @@
+@using System.Diagnostics
+
+@{
+ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First();
+}
+
+
+
+
+ Welcome to IdentityServer4
+ (version @version)
+
+
+
+
+ IdentityServer publishes a
+ discovery document
+ where you can find metadata and links to all the endpoints, key material, etc.
+
+
+ Click here to see the claims for your current session.
+
` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n @include font-size($font-size-base);\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Future-proof rule: in browsers that support :focus-visible, suppress the focus outline\n// on elements that programmatically receive focus but wouldn't normally show a visible\n// focus outline. In general, this would mean that the outline is only applied if the\n// interaction that led to the element receiving programmatic focus was a keyboard interaction,\n// or the browser has somehow determined that the user is primarily a keyboard user and/or\n// wants focus outlines to always be presented.\n//\n// See https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible\n// and https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `
`-`
` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable-next-line selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `
`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover() {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n\n @include hover() {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `
` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n // stylelint-disable-next-line property-blacklist\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// Remove the inheritance of word-wrap in Safari.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24990\nselect {\n word-wrap: normal;\n}\n\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\n[type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Opinionated: add \"hand\" cursor to non-disabled button elements.\n@if $enable-pointer-cursor-for-buttons {\n button,\n [type=\"button\"],\n [type=\"reset\"],\n [type=\"submit\"] {\n &:not(:disabled) {\n cursor: pointer;\n }\n }\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `
`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n @include font-size(1.5rem);\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","// Variables\n//\n// Variables should follow the `$component-state-property-size` formula for\n// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.\n\n// Color system\n\n$white: #fff !default;\n$gray-100: #f8f9fa !default;\n$gray-200: #e9ecef !default;\n$gray-300: #dee2e6 !default;\n$gray-400: #ced4da !default;\n$gray-500: #adb5bd !default;\n$gray-600: #6c757d !default;\n$gray-700: #495057 !default;\n$gray-800: #343a40 !default;\n$gray-900: #212529 !default;\n$black: #000 !default;\n\n$grays: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$grays: map-merge(\n (\n \"100\": $gray-100,\n \"200\": $gray-200,\n \"300\": $gray-300,\n \"400\": $gray-400,\n \"500\": $gray-500,\n \"600\": $gray-600,\n \"700\": $gray-700,\n \"800\": $gray-800,\n \"900\": $gray-900\n ),\n $grays\n);\n\n$blue: #007bff !default;\n$indigo: #6610f2 !default;\n$purple: #6f42c1 !default;\n$pink: #e83e8c !default;\n$red: #dc3545 !default;\n$orange: #fd7e14 !default;\n$yellow: #ffc107 !default;\n$green: #28a745 !default;\n$teal: #20c997 !default;\n$cyan: #17a2b8 !default;\n\n$colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$colors: map-merge(\n (\n \"blue\": $blue,\n \"indigo\": $indigo,\n \"purple\": $purple,\n \"pink\": $pink,\n \"red\": $red,\n \"orange\": $orange,\n \"yellow\": $yellow,\n \"green\": $green,\n \"teal\": $teal,\n \"cyan\": $cyan,\n \"white\": $white,\n \"gray\": $gray-600,\n \"gray-dark\": $gray-800\n ),\n $colors\n);\n\n$primary: $blue !default;\n$secondary: $gray-600 !default;\n$success: $green !default;\n$info: $cyan !default;\n$warning: $yellow !default;\n$danger: $red !default;\n$light: $gray-100 !default;\n$dark: $gray-800 !default;\n\n$theme-colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$theme-colors: map-merge(\n (\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n ),\n $theme-colors\n);\n\n// Set a specific jump point for requesting color jumps\n$theme-color-interval: 8% !default;\n\n// The yiq lightness value that determines when the lightness of color changes from \"dark\" to \"light\". Acceptable values are between 0 and 255.\n$yiq-contrasted-threshold: 150 !default;\n\n// Customize the light and dark text colors for use in our YIQ color contrast function.\n$yiq-text-dark: $gray-900 !default;\n$yiq-text-light: $white !default;\n\n// Characters which are escaped by the escape-svg function\n$escaped-characters: (\n (\"<\",\"%3c\"),\n (\">\",\"%3e\"),\n (\"#\",\"%23\"),\n) !default;\n\n\n// Options\n//\n// Quickly modify global styling by enabling or disabling optional features.\n\n$enable-caret: true !default;\n$enable-rounded: true !default;\n$enable-shadows: false !default;\n$enable-gradients: false !default;\n$enable-transitions: true !default;\n$enable-prefers-reduced-motion-media-query: true !default;\n$enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS\n$enable-grid-classes: true !default;\n$enable-pointer-cursor-for-buttons: true !default;\n$enable-print-styles: true !default;\n$enable-responsive-font-sizes: false !default;\n$enable-validation-icons: true !default;\n$enable-deprecation-messages: true !default;\n\n\n// Spacing\n//\n// Control the default styling of most Bootstrap elements by modifying these\n// variables. Mostly focused on spacing.\n// You can add more entries to the $spacers map, should you need more variation.\n\n$spacer: 1rem !default;\n$spacers: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$spacers: map-merge(\n (\n 0: 0,\n 1: ($spacer * .25),\n 2: ($spacer * .5),\n 3: $spacer,\n 4: ($spacer * 1.5),\n 5: ($spacer * 3)\n ),\n $spacers\n);\n\n// This variable affects the `.h-*` and `.w-*` classes.\n$sizes: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$sizes: map-merge(\n (\n 25: 25%,\n 50: 50%,\n 75: 75%,\n 100: 100%,\n auto: auto\n ),\n $sizes\n);\n\n\n// Body\n//\n// Settings for the `` element.\n\n$body-bg: $white !default;\n$body-color: $gray-900 !default;\n\n\n// Links\n//\n// Style anchor elements.\n\n$link-color: theme-color(\"primary\") !default;\n$link-decoration: none !default;\n$link-hover-color: darken($link-color, 15%) !default;\n$link-hover-decoration: underline !default;\n// Darken percentage for links with `.text-*` class (e.g. `.text-success`)\n$emphasized-link-hover-darken-percentage: 15% !default;\n\n// Paragraphs\n//\n// Style p element.\n\n$paragraph-margin-bottom: 1rem !default;\n\n\n// Grid breakpoints\n//\n// Define the minimum dimensions at which your layout will change,\n// adapting to different screen sizes, for use in media queries.\n\n$grid-breakpoints: (\n xs: 0,\n sm: 576px,\n md: 768px,\n lg: 992px,\n xl: 1200px\n) !default;\n\n@include _assert-ascending($grid-breakpoints, \"$grid-breakpoints\");\n@include _assert-starts-at-zero($grid-breakpoints, \"$grid-breakpoints\");\n\n\n// Grid containers\n//\n// Define the maximum width of `.container` for different screen sizes.\n\n$container-max-widths: (\n sm: 540px,\n md: 720px,\n lg: 960px,\n xl: 1140px\n) !default;\n\n@include _assert-ascending($container-max-widths, \"$container-max-widths\");\n\n\n// Grid columns\n//\n// Set the number of columns and specify the width of the gutters.\n\n$grid-columns: 12 !default;\n$grid-gutter-width: 30px !default;\n$grid-row-columns: 6 !default;\n\n\n// Components\n//\n// Define common padding and border radius sizes and more.\n\n$line-height-lg: 1.5 !default;\n$line-height-sm: 1.5 !default;\n\n$border-width: 1px !default;\n$border-color: $gray-300 !default;\n\n$border-radius: .25rem !default;\n$border-radius-lg: .3rem !default;\n$border-radius-sm: .2rem !default;\n\n$rounded-pill: 50rem !default;\n\n$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;\n$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;\n$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;\n\n$component-active-color: $white !default;\n$component-active-bg: theme-color(\"primary\") !default;\n\n$caret-width: .3em !default;\n$caret-vertical-align: $caret-width * .85 !default;\n$caret-spacing: $caret-width * .85 !default;\n\n$transition-base: all .2s ease-in-out !default;\n$transition-fade: opacity .15s linear !default;\n$transition-collapse: height .35s ease !default;\n\n$embed-responsive-aspect-ratios: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$embed-responsive-aspect-ratios: join(\n (\n (21 9),\n (16 9),\n (4 3),\n (1 1),\n ),\n $embed-responsive-aspect-ratios\n);\n\n// Typography\n//\n// Font, line-height, and color for body text, headings, and more.\n\n// stylelint-disable value-keyword-case\n$font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\" !default;\n$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !default;\n$font-family-base: $font-family-sans-serif !default;\n// stylelint-enable value-keyword-case\n\n$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`\n$font-size-lg: $font-size-base * 1.25 !default;\n$font-size-sm: $font-size-base * .875 !default;\n\n$font-weight-lighter: lighter !default;\n$font-weight-light: 300 !default;\n$font-weight-normal: 400 !default;\n$font-weight-bold: 700 !default;\n$font-weight-bolder: bolder !default;\n\n$font-weight-base: $font-weight-normal !default;\n$line-height-base: 1.5 !default;\n\n$h1-font-size: $font-size-base * 2.5 !default;\n$h2-font-size: $font-size-base * 2 !default;\n$h3-font-size: $font-size-base * 1.75 !default;\n$h4-font-size: $font-size-base * 1.5 !default;\n$h5-font-size: $font-size-base * 1.25 !default;\n$h6-font-size: $font-size-base !default;\n\n$headings-margin-bottom: $spacer / 2 !default;\n$headings-font-family: null !default;\n$headings-font-weight: 500 !default;\n$headings-line-height: 1.2 !default;\n$headings-color: null !default;\n\n$display1-size: 6rem !default;\n$display2-size: 5.5rem !default;\n$display3-size: 4.5rem !default;\n$display4-size: 3.5rem !default;\n\n$display1-weight: 300 !default;\n$display2-weight: 300 !default;\n$display3-weight: 300 !default;\n$display4-weight: 300 !default;\n$display-line-height: $headings-line-height !default;\n\n$lead-font-size: $font-size-base * 1.25 !default;\n$lead-font-weight: 300 !default;\n\n$small-font-size: 80% !default;\n\n$text-muted: $gray-600 !default;\n\n$blockquote-small-color: $gray-600 !default;\n$blockquote-small-font-size: $small-font-size !default;\n$blockquote-font-size: $font-size-base * 1.25 !default;\n\n$hr-border-color: rgba($black, .1) !default;\n$hr-border-width: $border-width !default;\n\n$mark-padding: .2em !default;\n\n$dt-font-weight: $font-weight-bold !default;\n\n$kbd-box-shadow: inset 0 -.1rem 0 rgba($black, .25) !default;\n$nested-kbd-font-weight: $font-weight-bold !default;\n\n$list-inline-padding: .5rem !default;\n\n$mark-bg: #fcf8e3 !default;\n\n$hr-margin-y: $spacer !default;\n\n\n// Tables\n//\n// Customizes the `.table` component with basic values, each used across all table variations.\n\n$table-cell-padding: .75rem !default;\n$table-cell-padding-sm: .3rem !default;\n\n$table-color: $body-color !default;\n$table-bg: null !default;\n$table-accent-bg: rgba($black, .05) !default;\n$table-hover-color: $table-color !default;\n$table-hover-bg: rgba($black, .075) !default;\n$table-active-bg: $table-hover-bg !default;\n\n$table-border-width: $border-width !default;\n$table-border-color: $border-color !default;\n\n$table-head-bg: $gray-200 !default;\n$table-head-color: $gray-700 !default;\n\n$table-dark-color: $white !default;\n$table-dark-bg: $gray-800 !default;\n$table-dark-accent-bg: rgba($white, .05) !default;\n$table-dark-hover-color: $table-dark-color !default;\n$table-dark-hover-bg: rgba($white, .075) !default;\n$table-dark-border-color: lighten($table-dark-bg, 7.5%) !default;\n\n$table-striped-order: odd !default;\n\n$table-caption-color: $text-muted !default;\n\n$table-bg-level: -9 !default;\n$table-border-level: -6 !default;\n\n\n// Buttons + Forms\n//\n// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.\n\n$input-btn-padding-y: .375rem !default;\n$input-btn-padding-x: .75rem !default;\n$input-btn-font-family: null !default;\n$input-btn-font-size: $font-size-base !default;\n$input-btn-line-height: $line-height-base !default;\n\n$input-btn-focus-width: .2rem !default;\n$input-btn-focus-color: rgba($component-active-bg, .25) !default;\n$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;\n\n$input-btn-padding-y-sm: .25rem !default;\n$input-btn-padding-x-sm: .5rem !default;\n$input-btn-font-size-sm: $font-size-sm !default;\n$input-btn-line-height-sm: $line-height-sm !default;\n\n$input-btn-padding-y-lg: .5rem !default;\n$input-btn-padding-x-lg: 1rem !default;\n$input-btn-font-size-lg: $font-size-lg !default;\n$input-btn-line-height-lg: $line-height-lg !default;\n\n$input-btn-border-width: $border-width !default;\n\n\n// Buttons\n//\n// For each of Bootstrap's buttons, define text, background, and border color.\n\n$btn-padding-y: $input-btn-padding-y !default;\n$btn-padding-x: $input-btn-padding-x !default;\n$btn-font-family: $input-btn-font-family !default;\n$btn-font-size: $input-btn-font-size !default;\n$btn-line-height: $input-btn-line-height !default;\n$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping\n\n$btn-padding-y-sm: $input-btn-padding-y-sm !default;\n$btn-padding-x-sm: $input-btn-padding-x-sm !default;\n$btn-font-size-sm: $input-btn-font-size-sm !default;\n$btn-line-height-sm: $input-btn-line-height-sm !default;\n\n$btn-padding-y-lg: $input-btn-padding-y-lg !default;\n$btn-padding-x-lg: $input-btn-padding-x-lg !default;\n$btn-font-size-lg: $input-btn-font-size-lg !default;\n$btn-line-height-lg: $input-btn-line-height-lg !default;\n\n$btn-border-width: $input-btn-border-width !default;\n\n$btn-font-weight: $font-weight-normal !default;\n$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;\n$btn-focus-width: $input-btn-focus-width !default;\n$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$btn-disabled-opacity: .65 !default;\n$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;\n\n$btn-link-disabled-color: $gray-600 !default;\n\n$btn-block-spacing-y: .5rem !default;\n\n// Allows for customizing button radius independently from global border radius\n$btn-border-radius: $border-radius !default;\n$btn-border-radius-lg: $border-radius-lg !default;\n$btn-border-radius-sm: $border-radius-sm !default;\n\n$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n\n// Forms\n\n$label-margin-bottom: .5rem !default;\n\n$input-padding-y: $input-btn-padding-y !default;\n$input-padding-x: $input-btn-padding-x !default;\n$input-font-family: $input-btn-font-family !default;\n$input-font-size: $input-btn-font-size !default;\n$input-font-weight: $font-weight-base !default;\n$input-line-height: $input-btn-line-height !default;\n\n$input-padding-y-sm: $input-btn-padding-y-sm !default;\n$input-padding-x-sm: $input-btn-padding-x-sm !default;\n$input-font-size-sm: $input-btn-font-size-sm !default;\n$input-line-height-sm: $input-btn-line-height-sm !default;\n\n$input-padding-y-lg: $input-btn-padding-y-lg !default;\n$input-padding-x-lg: $input-btn-padding-x-lg !default;\n$input-font-size-lg: $input-btn-font-size-lg !default;\n$input-line-height-lg: $input-btn-line-height-lg !default;\n\n$input-bg: $white !default;\n$input-disabled-bg: $gray-200 !default;\n\n$input-color: $gray-700 !default;\n$input-border-color: $gray-400 !default;\n$input-border-width: $input-btn-border-width !default;\n$input-box-shadow: inset 0 1px 1px rgba($black, .075) !default;\n\n$input-border-radius: $border-radius !default;\n$input-border-radius-lg: $border-radius-lg !default;\n$input-border-radius-sm: $border-radius-sm !default;\n\n$input-focus-bg: $input-bg !default;\n$input-focus-border-color: lighten($component-active-bg, 25%) !default;\n$input-focus-color: $input-color !default;\n$input-focus-width: $input-btn-focus-width !default;\n$input-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$input-placeholder-color: $gray-600 !default;\n$input-plaintext-color: $body-color !default;\n\n$input-height-border: $input-border-width * 2 !default;\n\n$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;\n$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;\n$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y / 2) !default;\n\n$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;\n$input-height-sm: add($input-line-height-sm * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;\n$input-height-lg: add($input-line-height-lg * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;\n\n$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$form-text-margin-top: .25rem !default;\n\n$form-check-input-gutter: 1.25rem !default;\n$form-check-input-margin-y: .3rem !default;\n$form-check-input-margin-x: .25rem !default;\n\n$form-check-inline-margin-x: .75rem !default;\n$form-check-inline-input-margin-x: .3125rem !default;\n\n$form-grid-gutter-width: 10px !default;\n$form-group-margin-bottom: 1rem !default;\n\n$input-group-addon-color: $input-color !default;\n$input-group-addon-bg: $gray-200 !default;\n$input-group-addon-border-color: $input-border-color !default;\n\n$custom-forms-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$custom-control-gutter: .5rem !default;\n$custom-control-spacer-x: 1rem !default;\n$custom-control-cursor: null !default;\n\n$custom-control-indicator-size: 1rem !default;\n$custom-control-indicator-bg: $input-bg !default;\n\n$custom-control-indicator-bg-size: 50% 50% !default;\n$custom-control-indicator-box-shadow: $input-box-shadow !default;\n$custom-control-indicator-border-color: $gray-500 !default;\n$custom-control-indicator-border-width: $input-border-width !default;\n\n$custom-control-label-color: null !default;\n\n$custom-control-indicator-disabled-bg: $input-disabled-bg !default;\n$custom-control-label-disabled-color: $gray-600 !default;\n\n$custom-control-indicator-checked-color: $component-active-color !default;\n$custom-control-indicator-checked-bg: $component-active-bg !default;\n$custom-control-indicator-checked-disabled-bg: rgba(theme-color(\"primary\"), .5) !default;\n$custom-control-indicator-checked-box-shadow: none !default;\n$custom-control-indicator-checked-border-color: $custom-control-indicator-checked-bg !default;\n\n$custom-control-indicator-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-control-indicator-focus-border-color: $input-focus-border-color !default;\n\n$custom-control-indicator-active-color: $component-active-color !default;\n$custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-control-indicator-active-box-shadow: none !default;\n$custom-control-indicator-active-border-color: $custom-control-indicator-active-bg !default;\n\n$custom-checkbox-indicator-border-radius: $border-radius !default;\n$custom-checkbox-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default;\n$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;\n$custom-checkbox-indicator-icon-indeterminate: url(\"data:image/svg+xml,\") !default;\n$custom-checkbox-indicator-indeterminate-box-shadow: none !default;\n$custom-checkbox-indicator-indeterminate-border-color: $custom-checkbox-indicator-indeterminate-bg !default;\n\n$custom-radio-indicator-border-radius: 50% !default;\n$custom-radio-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-switch-width: $custom-control-indicator-size * 1.75 !default;\n$custom-switch-indicator-border-radius: $custom-control-indicator-size / 2 !default;\n$custom-switch-indicator-size: subtract($custom-control-indicator-size, $custom-control-indicator-border-width * 4) !default;\n\n$custom-select-padding-y: $input-padding-y !default;\n$custom-select-padding-x: $input-padding-x !default;\n$custom-select-font-family: $input-font-family !default;\n$custom-select-font-size: $input-font-size !default;\n$custom-select-height: $input-height !default;\n$custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator\n$custom-select-font-weight: $input-font-weight !default;\n$custom-select-line-height: $input-line-height !default;\n$custom-select-color: $input-color !default;\n$custom-select-disabled-color: $gray-600 !default;\n$custom-select-bg: $input-bg !default;\n$custom-select-disabled-bg: $gray-200 !default;\n$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions\n$custom-select-indicator-color: $gray-800 !default;\n$custom-select-indicator: url(\"data:image/svg+xml,\") !default;\n$custom-select-background: escape-svg($custom-select-indicator) no-repeat right $custom-select-padding-x center / $custom-select-bg-size !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon)\n\n$custom-select-feedback-icon-padding-right: add(1em * .75, (2 * $custom-select-padding-y * .75) + $custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-position: center right ($custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;\n\n$custom-select-border-width: $input-border-width !default;\n$custom-select-border-color: $input-border-color !default;\n$custom-select-border-radius: $border-radius !default;\n$custom-select-box-shadow: inset 0 1px 2px rgba($black, .075) !default;\n\n$custom-select-focus-border-color: $input-focus-border-color !default;\n$custom-select-focus-width: $input-focus-width !default;\n$custom-select-focus-box-shadow: 0 0 0 $custom-select-focus-width $input-btn-focus-color !default;\n\n$custom-select-padding-y-sm: $input-padding-y-sm !default;\n$custom-select-padding-x-sm: $input-padding-x-sm !default;\n$custom-select-font-size-sm: $input-font-size-sm !default;\n$custom-select-height-sm: $input-height-sm !default;\n\n$custom-select-padding-y-lg: $input-padding-y-lg !default;\n$custom-select-padding-x-lg: $input-padding-x-lg !default;\n$custom-select-font-size-lg: $input-font-size-lg !default;\n$custom-select-height-lg: $input-height-lg !default;\n\n$custom-range-track-width: 100% !default;\n$custom-range-track-height: .5rem !default;\n$custom-range-track-cursor: pointer !default;\n$custom-range-track-bg: $gray-300 !default;\n$custom-range-track-border-radius: 1rem !default;\n$custom-range-track-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;\n\n$custom-range-thumb-width: 1rem !default;\n$custom-range-thumb-height: $custom-range-thumb-width !default;\n$custom-range-thumb-bg: $component-active-bg !default;\n$custom-range-thumb-border: 0 !default;\n$custom-range-thumb-border-radius: 1rem !default;\n$custom-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;\n$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;\n$custom-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in IE/Edge\n$custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-range-thumb-disabled-bg: $gray-500 !default;\n\n$custom-file-height: $input-height !default;\n$custom-file-height-inner: $input-height-inner !default;\n$custom-file-focus-border-color: $input-focus-border-color !default;\n$custom-file-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-file-disabled-bg: $input-disabled-bg !default;\n\n$custom-file-padding-y: $input-padding-y !default;\n$custom-file-padding-x: $input-padding-x !default;\n$custom-file-line-height: $input-line-height !default;\n$custom-file-font-family: $input-font-family !default;\n$custom-file-font-weight: $input-font-weight !default;\n$custom-file-color: $input-color !default;\n$custom-file-bg: $input-bg !default;\n$custom-file-border-width: $input-border-width !default;\n$custom-file-border-color: $input-border-color !default;\n$custom-file-border-radius: $input-border-radius !default;\n$custom-file-box-shadow: $input-box-shadow !default;\n$custom-file-button-color: $custom-file-color !default;\n$custom-file-button-bg: $input-group-addon-bg !default;\n$custom-file-text: (\n en: \"Browse\"\n) !default;\n\n\n// Form validation\n\n$form-feedback-margin-top: $form-text-margin-top !default;\n$form-feedback-font-size: $small-font-size !default;\n$form-feedback-valid-color: theme-color(\"success\") !default;\n$form-feedback-invalid-color: theme-color(\"danger\") !default;\n\n$form-feedback-icon-valid-color: $form-feedback-valid-color !default;\n$form-feedback-icon-valid: url(\"data:image/svg+xml,\") !default;\n$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;\n$form-feedback-icon-invalid: url(\"data:image/svg+xml,\") !default;\n\n$form-validation-states: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$form-validation-states: map-merge(\n (\n \"valid\": (\n \"color\": $form-feedback-valid-color,\n \"icon\": $form-feedback-icon-valid\n ),\n \"invalid\": (\n \"color\": $form-feedback-invalid-color,\n \"icon\": $form-feedback-icon-invalid\n ),\n ),\n $form-validation-states\n);\n\n// Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n\n$zindex-dropdown: 1000 !default;\n$zindex-sticky: 1020 !default;\n$zindex-fixed: 1030 !default;\n$zindex-modal-backdrop: 1040 !default;\n$zindex-modal: 1050 !default;\n$zindex-popover: 1060 !default;\n$zindex-tooltip: 1070 !default;\n\n\n// Navs\n\n$nav-link-padding-y: .5rem !default;\n$nav-link-padding-x: 1rem !default;\n$nav-link-disabled-color: $gray-600 !default;\n\n$nav-tabs-border-color: $gray-300 !default;\n$nav-tabs-border-width: $border-width !default;\n$nav-tabs-border-radius: $border-radius !default;\n$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default;\n$nav-tabs-link-active-color: $gray-700 !default;\n$nav-tabs-link-active-bg: $body-bg !default;\n$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;\n\n$nav-pills-border-radius: $border-radius !default;\n$nav-pills-link-active-color: $component-active-color !default;\n$nav-pills-link-active-bg: $component-active-bg !default;\n\n$nav-divider-color: $gray-200 !default;\n$nav-divider-margin-y: $spacer / 2 !default;\n\n\n// Navbar\n\n$navbar-padding-y: $spacer / 2 !default;\n$navbar-padding-x: $spacer !default;\n\n$navbar-nav-link-padding-x: .5rem !default;\n\n$navbar-brand-font-size: $font-size-lg !default;\n// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link\n$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;\n$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;\n$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;\n\n$navbar-toggler-padding-y: .25rem !default;\n$navbar-toggler-padding-x: .75rem !default;\n$navbar-toggler-font-size: $font-size-lg !default;\n$navbar-toggler-border-radius: $btn-border-radius !default;\n\n$navbar-dark-color: rgba($white, .5) !default;\n$navbar-dark-hover-color: rgba($white, .75) !default;\n$navbar-dark-active-color: $white !default;\n$navbar-dark-disabled-color: rgba($white, .25) !default;\n$navbar-dark-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-dark-toggler-border-color: rgba($white, .1) !default;\n\n$navbar-light-color: rgba($black, .5) !default;\n$navbar-light-hover-color: rgba($black, .7) !default;\n$navbar-light-active-color: rgba($black, .9) !default;\n$navbar-light-disabled-color: rgba($black, .3) !default;\n$navbar-light-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-light-toggler-border-color: rgba($black, .1) !default;\n\n$navbar-light-brand-color: $navbar-light-active-color !default;\n$navbar-light-brand-hover-color: $navbar-light-active-color !default;\n$navbar-dark-brand-color: $navbar-dark-active-color !default;\n$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;\n\n\n// Dropdowns\n//\n// Dropdown menu container and contents.\n\n$dropdown-min-width: 10rem !default;\n$dropdown-padding-y: .5rem !default;\n$dropdown-spacer: .125rem !default;\n$dropdown-font-size: $font-size-base !default;\n$dropdown-color: $body-color !default;\n$dropdown-bg: $white !default;\n$dropdown-border-color: rgba($black, .15) !default;\n$dropdown-border-radius: $border-radius !default;\n$dropdown-border-width: $border-width !default;\n$dropdown-inner-border-radius: subtract($dropdown-border-radius, $dropdown-border-width) !default;\n$dropdown-divider-bg: $gray-200 !default;\n$dropdown-divider-margin-y: $nav-divider-margin-y !default;\n$dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default;\n\n$dropdown-link-color: $gray-900 !default;\n$dropdown-link-hover-color: darken($gray-900, 5%) !default;\n$dropdown-link-hover-bg: $gray-100 !default;\n\n$dropdown-link-active-color: $component-active-color !default;\n$dropdown-link-active-bg: $component-active-bg !default;\n\n$dropdown-link-disabled-color: $gray-600 !default;\n\n$dropdown-item-padding-y: .25rem !default;\n$dropdown-item-padding-x: 1.5rem !default;\n\n$dropdown-header-color: $gray-600 !default;\n\n\n// Pagination\n\n$pagination-padding-y: .5rem !default;\n$pagination-padding-x: .75rem !default;\n$pagination-padding-y-sm: .25rem !default;\n$pagination-padding-x-sm: .5rem !default;\n$pagination-padding-y-lg: .75rem !default;\n$pagination-padding-x-lg: 1.5rem !default;\n$pagination-line-height: 1.25 !default;\n\n$pagination-color: $link-color !default;\n$pagination-bg: $white !default;\n$pagination-border-width: $border-width !default;\n$pagination-border-color: $gray-300 !default;\n\n$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$pagination-focus-outline: 0 !default;\n\n$pagination-hover-color: $link-hover-color !default;\n$pagination-hover-bg: $gray-200 !default;\n$pagination-hover-border-color: $gray-300 !default;\n\n$pagination-active-color: $component-active-color !default;\n$pagination-active-bg: $component-active-bg !default;\n$pagination-active-border-color: $pagination-active-bg !default;\n\n$pagination-disabled-color: $gray-600 !default;\n$pagination-disabled-bg: $white !default;\n$pagination-disabled-border-color: $gray-300 !default;\n\n\n// Jumbotron\n\n$jumbotron-padding: 2rem !default;\n$jumbotron-color: null !default;\n$jumbotron-bg: $gray-200 !default;\n\n\n// Cards\n\n$card-spacer-y: .75rem !default;\n$card-spacer-x: 1.25rem !default;\n$card-border-width: $border-width !default;\n$card-border-radius: $border-radius !default;\n$card-border-color: rgba($black, .125) !default;\n$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;\n$card-cap-bg: rgba($black, .03) !default;\n$card-cap-color: null !default;\n$card-height: null !default;\n$card-color: null !default;\n$card-bg: $white !default;\n\n$card-img-overlay-padding: 1.25rem !default;\n\n$card-group-margin: $grid-gutter-width / 2 !default;\n$card-deck-margin: $card-group-margin !default;\n\n$card-columns-count: 3 !default;\n$card-columns-gap: 1.25rem !default;\n$card-columns-margin: $card-spacer-y !default;\n\n\n// Tooltips\n\n$tooltip-font-size: $font-size-sm !default;\n$tooltip-max-width: 200px !default;\n$tooltip-color: $white !default;\n$tooltip-bg: $black !default;\n$tooltip-border-radius: $border-radius !default;\n$tooltip-opacity: .9 !default;\n$tooltip-padding-y: .25rem !default;\n$tooltip-padding-x: .5rem !default;\n$tooltip-margin: 0 !default;\n\n$tooltip-arrow-width: .8rem !default;\n$tooltip-arrow-height: .4rem !default;\n$tooltip-arrow-color: $tooltip-bg !default;\n\n// Form tooltips must come after regular tooltips\n$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;\n$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;\n$form-feedback-tooltip-font-size: $tooltip-font-size !default;\n$form-feedback-tooltip-line-height: $line-height-base !default;\n$form-feedback-tooltip-opacity: $tooltip-opacity !default;\n$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;\n\n\n// Popovers\n\n$popover-font-size: $font-size-sm !default;\n$popover-bg: $white !default;\n$popover-max-width: 276px !default;\n$popover-border-width: $border-width !default;\n$popover-border-color: rgba($black, .2) !default;\n$popover-border-radius: $border-radius-lg !default;\n$popover-inner-border-radius: subtract($popover-border-radius, $popover-border-width) !default;\n$popover-box-shadow: 0 .25rem .5rem rgba($black, .2) !default;\n\n$popover-header-bg: darken($popover-bg, 3%) !default;\n$popover-header-color: $headings-color !default;\n$popover-header-padding-y: .5rem !default;\n$popover-header-padding-x: .75rem !default;\n\n$popover-body-color: $body-color !default;\n$popover-body-padding-y: $popover-header-padding-y !default;\n$popover-body-padding-x: $popover-header-padding-x !default;\n\n$popover-arrow-width: 1rem !default;\n$popover-arrow-height: .5rem !default;\n$popover-arrow-color: $popover-bg !default;\n\n$popover-arrow-outer-color: fade-in($popover-border-color, .05) !default;\n\n\n// Toasts\n\n$toast-max-width: 350px !default;\n$toast-padding-x: .75rem !default;\n$toast-padding-y: .25rem !default;\n$toast-font-size: .875rem !default;\n$toast-color: null !default;\n$toast-background-color: rgba($white, .85) !default;\n$toast-border-width: 1px !default;\n$toast-border-color: rgba(0, 0, 0, .1) !default;\n$toast-border-radius: .25rem !default;\n$toast-box-shadow: 0 .25rem .75rem rgba($black, .1) !default;\n\n$toast-header-color: $gray-600 !default;\n$toast-header-background-color: rgba($white, .85) !default;\n$toast-header-border-color: rgba(0, 0, 0, .05) !default;\n\n\n// Badges\n\n$badge-font-size: 75% !default;\n$badge-font-weight: $font-weight-bold !default;\n$badge-padding-y: .25em !default;\n$badge-padding-x: .4em !default;\n$badge-border-radius: $border-radius !default;\n\n$badge-transition: $btn-transition !default;\n$badge-focus-width: $input-btn-focus-width !default;\n\n$badge-pill-padding-x: .6em !default;\n// Use a higher than normal value to ensure completely rounded edges when\n// customizing padding or font-size on labels.\n$badge-pill-border-radius: 10rem !default;\n\n\n// Modals\n\n// Padding applied to the modal body\n$modal-inner-padding: 1rem !default;\n\n// Margin between elements in footer, must be lower than or equal to 2 * $modal-inner-padding\n$modal-footer-margin-between: .5rem !default;\n\n$modal-dialog-margin: .5rem !default;\n$modal-dialog-margin-y-sm-up: 1.75rem !default;\n\n$modal-title-line-height: $line-height-base !default;\n\n$modal-content-color: null !default;\n$modal-content-bg: $white !default;\n$modal-content-border-color: rgba($black, .2) !default;\n$modal-content-border-width: $border-width !default;\n$modal-content-border-radius: $border-radius-lg !default;\n$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;\n$modal-content-box-shadow-xs: 0 .25rem .5rem rgba($black, .5) !default;\n$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;\n\n$modal-backdrop-bg: $black !default;\n$modal-backdrop-opacity: .5 !default;\n$modal-header-border-color: $border-color !default;\n$modal-footer-border-color: $modal-header-border-color !default;\n$modal-header-border-width: $modal-content-border-width !default;\n$modal-footer-border-width: $modal-header-border-width !default;\n$modal-header-padding-y: 1rem !default;\n$modal-header-padding-x: 1rem !default;\n$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility\n\n$modal-xl: 1140px !default;\n$modal-lg: 800px !default;\n$modal-md: 500px !default;\n$modal-sm: 300px !default;\n\n$modal-fade-transform: translate(0, -50px) !default;\n$modal-show-transform: none !default;\n$modal-transition: transform .3s ease-out !default;\n$modal-scale-transform: scale(1.02) !default;\n\n\n// Alerts\n//\n// Define alert colors, border radius, and padding.\n\n$alert-padding-y: .75rem !default;\n$alert-padding-x: 1.25rem !default;\n$alert-margin-bottom: 1rem !default;\n$alert-border-radius: $border-radius !default;\n$alert-link-font-weight: $font-weight-bold !default;\n$alert-border-width: $border-width !default;\n\n$alert-bg-level: -10 !default;\n$alert-border-level: -9 !default;\n$alert-color-level: 6 !default;\n\n\n// Progress bars\n\n$progress-height: 1rem !default;\n$progress-font-size: $font-size-base * .75 !default;\n$progress-bg: $gray-200 !default;\n$progress-border-radius: $border-radius !default;\n$progress-box-shadow: inset 0 .1rem .1rem rgba($black, .1) !default;\n$progress-bar-color: $white !default;\n$progress-bar-bg: theme-color(\"primary\") !default;\n$progress-bar-animation-timing: 1s linear infinite !default;\n$progress-bar-transition: width .6s ease !default;\n\n\n// List group\n\n$list-group-color: null !default;\n$list-group-bg: $white !default;\n$list-group-border-color: rgba($black, .125) !default;\n$list-group-border-width: $border-width !default;\n$list-group-border-radius: $border-radius !default;\n\n$list-group-item-padding-y: .75rem !default;\n$list-group-item-padding-x: 1.25rem !default;\n\n$list-group-hover-bg: $gray-100 !default;\n$list-group-active-color: $component-active-color !default;\n$list-group-active-bg: $component-active-bg !default;\n$list-group-active-border-color: $list-group-active-bg !default;\n\n$list-group-disabled-color: $gray-600 !default;\n$list-group-disabled-bg: $list-group-bg !default;\n\n$list-group-action-color: $gray-700 !default;\n$list-group-action-hover-color: $list-group-action-color !default;\n\n$list-group-action-active-color: $body-color !default;\n$list-group-action-active-bg: $gray-200 !default;\n\n\n// Image thumbnails\n\n$thumbnail-padding: .25rem !default;\n$thumbnail-bg: $body-bg !default;\n$thumbnail-border-width: $border-width !default;\n$thumbnail-border-color: $gray-300 !default;\n$thumbnail-border-radius: $border-radius !default;\n$thumbnail-box-shadow: 0 1px 2px rgba($black, .075) !default;\n\n\n// Figures\n\n$figure-caption-font-size: 90% !default;\n$figure-caption-color: $gray-600 !default;\n\n\n// Breadcrumbs\n\n$breadcrumb-font-size: null !default;\n\n$breadcrumb-padding-y: .75rem !default;\n$breadcrumb-padding-x: 1rem !default;\n$breadcrumb-item-padding: .5rem !default;\n\n$breadcrumb-margin-bottom: 1rem !default;\n\n$breadcrumb-bg: $gray-200 !default;\n$breadcrumb-divider-color: $gray-600 !default;\n$breadcrumb-active-color: $gray-600 !default;\n$breadcrumb-divider: quote(\"/\") !default;\n\n$breadcrumb-border-radius: $border-radius !default;\n\n\n// Carousel\n\n$carousel-control-color: $white !default;\n$carousel-control-width: 15% !default;\n$carousel-control-opacity: .5 !default;\n$carousel-control-hover-opacity: .9 !default;\n$carousel-control-transition: opacity .15s ease !default;\n\n$carousel-indicator-width: 30px !default;\n$carousel-indicator-height: 3px !default;\n$carousel-indicator-hit-area-height: 10px !default;\n$carousel-indicator-spacer: 3px !default;\n$carousel-indicator-active-bg: $white !default;\n$carousel-indicator-transition: opacity .6s ease !default;\n\n$carousel-caption-width: 70% !default;\n$carousel-caption-color: $white !default;\n\n$carousel-control-icon-width: 20px !default;\n\n$carousel-control-prev-icon-bg: url(\"data:image/svg+xml,\") !default;\n$carousel-control-next-icon-bg: url(\"data:image/svg+xml,\") !default;\n\n$carousel-transition-duration: .6s !default;\n$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)\n\n\n// Spinners\n\n$spinner-width: 2rem !default;\n$spinner-height: $spinner-width !default;\n$spinner-border-width: .25em !default;\n\n$spinner-width-sm: 1rem !default;\n$spinner-height-sm: $spinner-width-sm !default;\n$spinner-border-width-sm: .2em !default;\n\n\n// Close\n\n$close-font-size: $font-size-base * 1.5 !default;\n$close-font-weight: $font-weight-bold !default;\n$close-color: $black !default;\n$close-text-shadow: 0 1px 0 $white !default;\n\n\n// Code\n\n$code-font-size: 87.5% !default;\n$code-color: $pink !default;\n\n$kbd-padding-y: .2rem !default;\n$kbd-padding-x: .4rem !default;\n$kbd-font-size: $code-font-size !default;\n$kbd-color: $white !default;\n$kbd-bg: $gray-900 !default;\n\n$pre-color: $gray-900 !default;\n$pre-scrollable-max-height: 340px !default;\n\n\n// Utilities\n\n$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex !default;\n$overflows: auto, hidden !default;\n$positions: static, relative, absolute, fixed, sticky !default;\n\n\n// Printing\n\n$print-page-size: a3 !default;\n$print-body-min-width: map-get($grid-breakpoints, \"lg\") !default;\n","// stylelint-disable property-blacklist, scss/dollar-variable-default\n\n// SCSS RFS mixin\n//\n// Automated font-resizing\n//\n// See https://github.com/twbs/rfs\n\n// Configuration\n\n// Base font size\n$rfs-base-font-size: 1.25rem !default;\n$rfs-font-size-unit: rem !default;\n\n// Breakpoint at where font-size starts decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n// Resize font-size based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != \"number\" or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-responsive-font-sizes to false\n$enable-responsive-font-sizes: true !default;\n\n// Cache $rfs-base-font-size unit\n$rfs-base-font-size-unit: unit($rfs-base-font-size);\n\n// Remove px-unit from $rfs-base-font-size for calculations\n@if $rfs-base-font-size-unit == \"px\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1);\n}\n@else if $rfs-base-font-size-unit == \"rem\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1 / $rfs-rem-value);\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == \"px\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == \"rem\" or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value);\n}\n\n// Responsive font-size mixin\n@mixin rfs($fs, $important: false) {\n // Cache $fs unit\n $fs-unit: if(type-of($fs) == \"number\", unit($fs), false);\n\n // Add !important suffix if needed\n $rfs-suffix: if($important, \" !important\", \"\");\n\n // If $fs isn't a number (like inherit) or $fs has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $fs-unit or $fs-unit != \"\" and $fs-unit != \"px\" and $fs-unit != \"rem\" or $fs == 0 {\n font-size: #{$fs}#{$rfs-suffix};\n }\n @else {\n // Variables for storing static and fluid rescaling\n $rfs-static: null;\n $rfs-fluid: null;\n\n // Remove px-unit from $fs for calculations\n @if $fs-unit == \"px\" {\n $fs: $fs / ($fs * 0 + 1);\n }\n @else if $fs-unit == \"rem\" {\n $fs: $fs / ($fs * 0 + 1 / $rfs-rem-value);\n }\n\n // Set default font-size\n @if $rfs-font-size-unit == rem {\n $rfs-static: #{$fs / $rfs-rem-value}rem#{$rfs-suffix};\n }\n @else if $rfs-font-size-unit == px {\n $rfs-static: #{$fs}px#{$rfs-suffix};\n }\n @else {\n @error \"`#{$rfs-font-size-unit}` is not a valid unit for $rfs-font-size-unit. Use `px` or `rem`.\";\n }\n\n // Only add media query if font-size is bigger as the minimum font-size\n // If $rfs-factor == 1, no rescaling will take place\n @if $fs > $rfs-base-font-size and $enable-responsive-font-sizes {\n $min-width: null;\n $variable-unit: null;\n\n // Calculate minimum font-size for given font-size\n $fs-min: $rfs-base-font-size + ($fs - $rfs-base-font-size) / $rfs-factor;\n\n // Calculate difference between given font-size and minimum font-size for given font-size\n $fs-diff: $fs - $fs-min;\n\n // Base font-size formatting\n // No need to check if the unit is valid, because we did that before\n $min-width: if($rfs-font-size-unit == rem, #{$fs-min / $rfs-rem-value}rem, #{$fs-min}px);\n\n // If two-dimensional, use smallest of screen width and height\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{$fs-diff * 100 / $rfs-breakpoint}#{$variable-unit};\n\n // Set the calculated font-size.\n $rfs-fluid: calc(#{$min-width} + #{$variable-width}) #{$rfs-suffix};\n }\n\n // Rendering\n @if $rfs-fluid == null {\n // Only render static font-size if no fluid font-size is available\n font-size: $rfs-static;\n }\n @else {\n $mq-value: null;\n\n // RFS breakpoint formatting\n @if $rfs-breakpoint-unit == em or $rfs-breakpoint-unit == rem {\n $mq-value: #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit};\n }\n @else if $rfs-breakpoint-unit == px {\n $mq-value: #{$rfs-breakpoint}px;\n }\n @else {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n }\n\n @if $rfs-class == \"disable\" {\n // Adding an extra class increases specificity,\n // which prevents the media query to override the font size\n &,\n .disable-responsive-font-size &,\n &.disable-responsive-font-size {\n font-size: $rfs-static;\n }\n }\n @else {\n font-size: $rfs-static;\n }\n\n @if $rfs-two-dimensional {\n @media (max-width: #{$mq-value}), (max-height: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n @else {\n @media (max-width: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n }\n }\n}\n\n// The font-size & responsive-font-size mixin uses RFS to rescale font sizes\n@mixin font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n\n@mixin responsive-font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover() {\n &:hover { @content; }\n}\n\n@mixin hover-focus() {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus() {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active() {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n"]}
\ No newline at end of file
diff --git a/IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css b/IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
new file mode 100644
index 0000000..5308df6
--- /dev/null
+++ b/IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css
@@ -0,0 +1,8 @@
+/*!
+ * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)
+ * Copyright 2011-2019 The Bootstrap Authors
+ * Copyright 2011-2019 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
+ */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
+/*# sourceMappingURL=bootstrap-reboot.min.css.map */
\ No newline at end of file
diff --git a/IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map b/IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map
new file mode 100644
index 0000000..b8551f7
--- /dev/null
+++ b/IdentityServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../../scss/bootstrap-reboot.scss","../../scss/_reboot.scss","dist/css/bootstrap-reboot.css","../../scss/vendor/_rfs.scss","bootstrap-reboot.css","../../scss/mixins/_hover.scss"],"names":[],"mappings":"AAAA;;;;;;ACkBA,ECTA,QADA,SDaE,WAAA,WAGF,KACE,YAAA,WACA,YAAA,KACA,yBAAA,KACA,4BAAA,YAMF,QAAA,MAAA,WAAA,OAAA,OAAA,OAAA,OAAA,KAAA,IAAA,QACE,QAAA,MAUF,KACE,OAAA,EACA,YAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,WAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBEgFI,UAAA,KF9EJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,KACA,iBAAA,KGlBF,0CH+BE,QAAA,YASF,GACE,WAAA,YACA,OAAA,EACA,SAAA,QAaF,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAOF,EACE,WAAA,EACA,cAAA,KC9CF,0BDyDA,YAEE,gBAAA,UACA,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,cAAA,EACA,iCAAA,KAAA,yBAAA,KAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QCnDF,GDsDA,GCvDA,GD0DE,WAAA,EACA,cAAA,KAGF,MCtDA,MACA,MAFA,MD2DE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAGF,ECvDA,ODyDE,YAAA,OAGF,MExFI,UAAA,IFiGJ,IC5DA,ID8DE,SAAA,SEnGE,UAAA,IFqGF,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAON,EACE,MAAA,QACA,gBAAA,KACA,iBAAA,YIhLA,QJmLE,MAAA,QACA,gBAAA,UASJ,cACE,MAAA,QACA,gBAAA,KI/LA,oBJkME,MAAA,QACA,gBAAA,KC7DJ,KACA,IDqEA,ICpEA,KDwEE,YAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UEpJE,UAAA,IFwJJ,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAQF,OAEE,OAAA,EAAA,EAAA,KAQF,IACE,eAAA,OACA,aAAA,KAGF,IAGE,SAAA,OACA,eAAA,OAQF,MACE,gBAAA,SAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAGE,WAAA,QAQF,MAEE,QAAA,aACA,cAAA,MAMF,OAEE,cAAA,EAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBCxGF,OD2GA,MCzGA,SADA,OAEA,SD6GE,OAAA,EACA,YAAA,QErPE,UAAA,QFuPF,YAAA,QAGF,OC3GA,MD6GE,SAAA,QAGF,OC3GA,OD6GE,eAAA,KAMF,OACE,UAAA,OC3GF,cACA,aACA,cDgHA,OAIE,mBAAA,OC/GF,6BACA,4BACA,6BDkHE,sBAKI,OAAA,QClHN,gCACA,+BACA,gCDsHA,yBAIE,QAAA,EACA,aAAA,KCrHF,qBDwHA,kBAEE,WAAA,WACA,QAAA,EAIF,iBCxHA,2BACA,kBAFA,iBDkIE,mBAAA,QAGF,SACE,SAAA,KAEA,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAKF,OACE,QAAA,MACA,MAAA,KACA,UAAA,KACA,QAAA,EACA,cAAA,MEjSI,UAAA,OFmSJ,YAAA,QACA,MAAA,QACA,YAAA,OAGF,SACE,eAAA,SGvIF,yCFGA,yCD0IE,OAAA,KGxIF,cHgJE,eAAA,KACA,mBAAA,KG5IF,yCHoJE,mBAAA,KAQF,6BACE,KAAA,QACA,mBAAA,OAOF,OACE,QAAA,aAGF,QACE,QAAA,UACA,OAAA,QAGF,SACE,QAAA,KGzJF,SH+JE,QAAA","sourcesContent":["/*!\n * Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"reboot\";\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -webkit-tap-highlight-color: rgba($black, 0); // 5\n}\n\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\n// TODO: remove in v5\n// stylelint-disable-next-line selector-list-comma-newline-after\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use\n// the `inherit` value on things like `
` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n @include font-size($font-size-base);\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Future-proof rule: in browsers that support :focus-visible, suppress the focus outline\n// on elements that programmatically receive focus but wouldn't normally show a visible\n// focus outline. In general, this would mean that the outline is only applied if the\n// interaction that led to the element receiving programmatic focus was a keyboard interaction,\n// or the browser has somehow determined that the user is primarily a keyboard user and/or\n// wants focus outlines to always be presented.\n//\n// See https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible\n// and https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `
`-`
` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable-next-line selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `
`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover() {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n\n @include hover() {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `
` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n // stylelint-disable-next-line property-blacklist\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// Remove the inheritance of word-wrap in Safari.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24990\nselect {\n word-wrap: normal;\n}\n\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\n[type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Opinionated: add \"hand\" cursor to non-disabled button elements.\n@if $enable-pointer-cursor-for-buttons {\n button,\n [type=\"button\"],\n [type=\"reset\"],\n [type=\"submit\"] {\n &:not(:disabled) {\n cursor: pointer;\n }\n }\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `
` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n @include font-size($font-size-base);\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Future-proof rule: in browsers that support :focus-visible, suppress the focus outline\n// on elements that programmatically receive focus but wouldn't normally show a visible\n// focus outline. In general, this would mean that the outline is only applied if the\n// interaction that led to the element receiving programmatic focus was a keyboard interaction,\n// or the browser has somehow determined that the user is primarily a keyboard user and/or\n// wants focus outlines to always be presented.\n//\n// See https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible\n// and https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/\n[tabindex=\"-1\"]:focus:not(:focus-visible) {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `
`-`
` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable-next-line selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `
`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover() {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]) {\n color: inherit;\n text-decoration: none;\n\n @include hover() {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `
` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n // stylelint-disable-next-line property-blacklist\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// Remove the inheritance of word-wrap in Safari.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24990\nselect {\n word-wrap: normal;\n}\n\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\n[type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Opinionated: add \"hand\" cursor to non-disabled button elements.\n@if $enable-pointer-cursor-for-buttons {\n button,\n [type=\"button\"],\n [type=\"reset\"],\n [type=\"submit\"] {\n &:not(:disabled) {\n cursor: pointer;\n }\n }\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `
`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n @include font-size(1.5rem);\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","// Variables\n//\n// Variables should follow the `$component-state-property-size` formula for\n// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.\n\n// Color system\n\n$white: #fff !default;\n$gray-100: #f8f9fa !default;\n$gray-200: #e9ecef !default;\n$gray-300: #dee2e6 !default;\n$gray-400: #ced4da !default;\n$gray-500: #adb5bd !default;\n$gray-600: #6c757d !default;\n$gray-700: #495057 !default;\n$gray-800: #343a40 !default;\n$gray-900: #212529 !default;\n$black: #000 !default;\n\n$grays: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$grays: map-merge(\n (\n \"100\": $gray-100,\n \"200\": $gray-200,\n \"300\": $gray-300,\n \"400\": $gray-400,\n \"500\": $gray-500,\n \"600\": $gray-600,\n \"700\": $gray-700,\n \"800\": $gray-800,\n \"900\": $gray-900\n ),\n $grays\n);\n\n$blue: #007bff !default;\n$indigo: #6610f2 !default;\n$purple: #6f42c1 !default;\n$pink: #e83e8c !default;\n$red: #dc3545 !default;\n$orange: #fd7e14 !default;\n$yellow: #ffc107 !default;\n$green: #28a745 !default;\n$teal: #20c997 !default;\n$cyan: #17a2b8 !default;\n\n$colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$colors: map-merge(\n (\n \"blue\": $blue,\n \"indigo\": $indigo,\n \"purple\": $purple,\n \"pink\": $pink,\n \"red\": $red,\n \"orange\": $orange,\n \"yellow\": $yellow,\n \"green\": $green,\n \"teal\": $teal,\n \"cyan\": $cyan,\n \"white\": $white,\n \"gray\": $gray-600,\n \"gray-dark\": $gray-800\n ),\n $colors\n);\n\n$primary: $blue !default;\n$secondary: $gray-600 !default;\n$success: $green !default;\n$info: $cyan !default;\n$warning: $yellow !default;\n$danger: $red !default;\n$light: $gray-100 !default;\n$dark: $gray-800 !default;\n\n$theme-colors: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$theme-colors: map-merge(\n (\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n ),\n $theme-colors\n);\n\n// Set a specific jump point for requesting color jumps\n$theme-color-interval: 8% !default;\n\n// The yiq lightness value that determines when the lightness of color changes from \"dark\" to \"light\". Acceptable values are between 0 and 255.\n$yiq-contrasted-threshold: 150 !default;\n\n// Customize the light and dark text colors for use in our YIQ color contrast function.\n$yiq-text-dark: $gray-900 !default;\n$yiq-text-light: $white !default;\n\n// Characters which are escaped by the escape-svg function\n$escaped-characters: (\n (\"<\",\"%3c\"),\n (\">\",\"%3e\"),\n (\"#\",\"%23\"),\n) !default;\n\n\n// Options\n//\n// Quickly modify global styling by enabling or disabling optional features.\n\n$enable-caret: true !default;\n$enable-rounded: true !default;\n$enable-shadows: false !default;\n$enable-gradients: false !default;\n$enable-transitions: true !default;\n$enable-prefers-reduced-motion-media-query: true !default;\n$enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS\n$enable-grid-classes: true !default;\n$enable-pointer-cursor-for-buttons: true !default;\n$enable-print-styles: true !default;\n$enable-responsive-font-sizes: false !default;\n$enable-validation-icons: true !default;\n$enable-deprecation-messages: true !default;\n\n\n// Spacing\n//\n// Control the default styling of most Bootstrap elements by modifying these\n// variables. Mostly focused on spacing.\n// You can add more entries to the $spacers map, should you need more variation.\n\n$spacer: 1rem !default;\n$spacers: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$spacers: map-merge(\n (\n 0: 0,\n 1: ($spacer * .25),\n 2: ($spacer * .5),\n 3: $spacer,\n 4: ($spacer * 1.5),\n 5: ($spacer * 3)\n ),\n $spacers\n);\n\n// This variable affects the `.h-*` and `.w-*` classes.\n$sizes: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$sizes: map-merge(\n (\n 25: 25%,\n 50: 50%,\n 75: 75%,\n 100: 100%,\n auto: auto\n ),\n $sizes\n);\n\n\n// Body\n//\n// Settings for the `` element.\n\n$body-bg: $white !default;\n$body-color: $gray-900 !default;\n\n\n// Links\n//\n// Style anchor elements.\n\n$link-color: theme-color(\"primary\") !default;\n$link-decoration: none !default;\n$link-hover-color: darken($link-color, 15%) !default;\n$link-hover-decoration: underline !default;\n// Darken percentage for links with `.text-*` class (e.g. `.text-success`)\n$emphasized-link-hover-darken-percentage: 15% !default;\n\n// Paragraphs\n//\n// Style p element.\n\n$paragraph-margin-bottom: 1rem !default;\n\n\n// Grid breakpoints\n//\n// Define the minimum dimensions at which your layout will change,\n// adapting to different screen sizes, for use in media queries.\n\n$grid-breakpoints: (\n xs: 0,\n sm: 576px,\n md: 768px,\n lg: 992px,\n xl: 1200px\n) !default;\n\n@include _assert-ascending($grid-breakpoints, \"$grid-breakpoints\");\n@include _assert-starts-at-zero($grid-breakpoints, \"$grid-breakpoints\");\n\n\n// Grid containers\n//\n// Define the maximum width of `.container` for different screen sizes.\n\n$container-max-widths: (\n sm: 540px,\n md: 720px,\n lg: 960px,\n xl: 1140px\n) !default;\n\n@include _assert-ascending($container-max-widths, \"$container-max-widths\");\n\n\n// Grid columns\n//\n// Set the number of columns and specify the width of the gutters.\n\n$grid-columns: 12 !default;\n$grid-gutter-width: 30px !default;\n$grid-row-columns: 6 !default;\n\n\n// Components\n//\n// Define common padding and border radius sizes and more.\n\n$line-height-lg: 1.5 !default;\n$line-height-sm: 1.5 !default;\n\n$border-width: 1px !default;\n$border-color: $gray-300 !default;\n\n$border-radius: .25rem !default;\n$border-radius-lg: .3rem !default;\n$border-radius-sm: .2rem !default;\n\n$rounded-pill: 50rem !default;\n\n$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;\n$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;\n$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;\n\n$component-active-color: $white !default;\n$component-active-bg: theme-color(\"primary\") !default;\n\n$caret-width: .3em !default;\n$caret-vertical-align: $caret-width * .85 !default;\n$caret-spacing: $caret-width * .85 !default;\n\n$transition-base: all .2s ease-in-out !default;\n$transition-fade: opacity .15s linear !default;\n$transition-collapse: height .35s ease !default;\n\n$embed-responsive-aspect-ratios: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$embed-responsive-aspect-ratios: join(\n (\n (21 9),\n (16 9),\n (4 3),\n (1 1),\n ),\n $embed-responsive-aspect-ratios\n);\n\n// Typography\n//\n// Font, line-height, and color for body text, headings, and more.\n\n// stylelint-disable value-keyword-case\n$font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\" !default;\n$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !default;\n$font-family-base: $font-family-sans-serif !default;\n// stylelint-enable value-keyword-case\n\n$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`\n$font-size-lg: $font-size-base * 1.25 !default;\n$font-size-sm: $font-size-base * .875 !default;\n\n$font-weight-lighter: lighter !default;\n$font-weight-light: 300 !default;\n$font-weight-normal: 400 !default;\n$font-weight-bold: 700 !default;\n$font-weight-bolder: bolder !default;\n\n$font-weight-base: $font-weight-normal !default;\n$line-height-base: 1.5 !default;\n\n$h1-font-size: $font-size-base * 2.5 !default;\n$h2-font-size: $font-size-base * 2 !default;\n$h3-font-size: $font-size-base * 1.75 !default;\n$h4-font-size: $font-size-base * 1.5 !default;\n$h5-font-size: $font-size-base * 1.25 !default;\n$h6-font-size: $font-size-base !default;\n\n$headings-margin-bottom: $spacer / 2 !default;\n$headings-font-family: null !default;\n$headings-font-weight: 500 !default;\n$headings-line-height: 1.2 !default;\n$headings-color: null !default;\n\n$display1-size: 6rem !default;\n$display2-size: 5.5rem !default;\n$display3-size: 4.5rem !default;\n$display4-size: 3.5rem !default;\n\n$display1-weight: 300 !default;\n$display2-weight: 300 !default;\n$display3-weight: 300 !default;\n$display4-weight: 300 !default;\n$display-line-height: $headings-line-height !default;\n\n$lead-font-size: $font-size-base * 1.25 !default;\n$lead-font-weight: 300 !default;\n\n$small-font-size: 80% !default;\n\n$text-muted: $gray-600 !default;\n\n$blockquote-small-color: $gray-600 !default;\n$blockquote-small-font-size: $small-font-size !default;\n$blockquote-font-size: $font-size-base * 1.25 !default;\n\n$hr-border-color: rgba($black, .1) !default;\n$hr-border-width: $border-width !default;\n\n$mark-padding: .2em !default;\n\n$dt-font-weight: $font-weight-bold !default;\n\n$kbd-box-shadow: inset 0 -.1rem 0 rgba($black, .25) !default;\n$nested-kbd-font-weight: $font-weight-bold !default;\n\n$list-inline-padding: .5rem !default;\n\n$mark-bg: #fcf8e3 !default;\n\n$hr-margin-y: $spacer !default;\n\n\n// Tables\n//\n// Customizes the `.table` component with basic values, each used across all table variations.\n\n$table-cell-padding: .75rem !default;\n$table-cell-padding-sm: .3rem !default;\n\n$table-color: $body-color !default;\n$table-bg: null !default;\n$table-accent-bg: rgba($black, .05) !default;\n$table-hover-color: $table-color !default;\n$table-hover-bg: rgba($black, .075) !default;\n$table-active-bg: $table-hover-bg !default;\n\n$table-border-width: $border-width !default;\n$table-border-color: $border-color !default;\n\n$table-head-bg: $gray-200 !default;\n$table-head-color: $gray-700 !default;\n\n$table-dark-color: $white !default;\n$table-dark-bg: $gray-800 !default;\n$table-dark-accent-bg: rgba($white, .05) !default;\n$table-dark-hover-color: $table-dark-color !default;\n$table-dark-hover-bg: rgba($white, .075) !default;\n$table-dark-border-color: lighten($table-dark-bg, 7.5%) !default;\n\n$table-striped-order: odd !default;\n\n$table-caption-color: $text-muted !default;\n\n$table-bg-level: -9 !default;\n$table-border-level: -6 !default;\n\n\n// Buttons + Forms\n//\n// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.\n\n$input-btn-padding-y: .375rem !default;\n$input-btn-padding-x: .75rem !default;\n$input-btn-font-family: null !default;\n$input-btn-font-size: $font-size-base !default;\n$input-btn-line-height: $line-height-base !default;\n\n$input-btn-focus-width: .2rem !default;\n$input-btn-focus-color: rgba($component-active-bg, .25) !default;\n$input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;\n\n$input-btn-padding-y-sm: .25rem !default;\n$input-btn-padding-x-sm: .5rem !default;\n$input-btn-font-size-sm: $font-size-sm !default;\n$input-btn-line-height-sm: $line-height-sm !default;\n\n$input-btn-padding-y-lg: .5rem !default;\n$input-btn-padding-x-lg: 1rem !default;\n$input-btn-font-size-lg: $font-size-lg !default;\n$input-btn-line-height-lg: $line-height-lg !default;\n\n$input-btn-border-width: $border-width !default;\n\n\n// Buttons\n//\n// For each of Bootstrap's buttons, define text, background, and border color.\n\n$btn-padding-y: $input-btn-padding-y !default;\n$btn-padding-x: $input-btn-padding-x !default;\n$btn-font-family: $input-btn-font-family !default;\n$btn-font-size: $input-btn-font-size !default;\n$btn-line-height: $input-btn-line-height !default;\n$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping\n\n$btn-padding-y-sm: $input-btn-padding-y-sm !default;\n$btn-padding-x-sm: $input-btn-padding-x-sm !default;\n$btn-font-size-sm: $input-btn-font-size-sm !default;\n$btn-line-height-sm: $input-btn-line-height-sm !default;\n\n$btn-padding-y-lg: $input-btn-padding-y-lg !default;\n$btn-padding-x-lg: $input-btn-padding-x-lg !default;\n$btn-font-size-lg: $input-btn-font-size-lg !default;\n$btn-line-height-lg: $input-btn-line-height-lg !default;\n\n$btn-border-width: $input-btn-border-width !default;\n\n$btn-font-weight: $font-weight-normal !default;\n$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;\n$btn-focus-width: $input-btn-focus-width !default;\n$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$btn-disabled-opacity: .65 !default;\n$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;\n\n$btn-link-disabled-color: $gray-600 !default;\n\n$btn-block-spacing-y: .5rem !default;\n\n// Allows for customizing button radius independently from global border radius\n$btn-border-radius: $border-radius !default;\n$btn-border-radius-lg: $border-radius-lg !default;\n$btn-border-radius-sm: $border-radius-sm !default;\n\n$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n\n// Forms\n\n$label-margin-bottom: .5rem !default;\n\n$input-padding-y: $input-btn-padding-y !default;\n$input-padding-x: $input-btn-padding-x !default;\n$input-font-family: $input-btn-font-family !default;\n$input-font-size: $input-btn-font-size !default;\n$input-font-weight: $font-weight-base !default;\n$input-line-height: $input-btn-line-height !default;\n\n$input-padding-y-sm: $input-btn-padding-y-sm !default;\n$input-padding-x-sm: $input-btn-padding-x-sm !default;\n$input-font-size-sm: $input-btn-font-size-sm !default;\n$input-line-height-sm: $input-btn-line-height-sm !default;\n\n$input-padding-y-lg: $input-btn-padding-y-lg !default;\n$input-padding-x-lg: $input-btn-padding-x-lg !default;\n$input-font-size-lg: $input-btn-font-size-lg !default;\n$input-line-height-lg: $input-btn-line-height-lg !default;\n\n$input-bg: $white !default;\n$input-disabled-bg: $gray-200 !default;\n\n$input-color: $gray-700 !default;\n$input-border-color: $gray-400 !default;\n$input-border-width: $input-btn-border-width !default;\n$input-box-shadow: inset 0 1px 1px rgba($black, .075) !default;\n\n$input-border-radius: $border-radius !default;\n$input-border-radius-lg: $border-radius-lg !default;\n$input-border-radius-sm: $border-radius-sm !default;\n\n$input-focus-bg: $input-bg !default;\n$input-focus-border-color: lighten($component-active-bg, 25%) !default;\n$input-focus-color: $input-color !default;\n$input-focus-width: $input-btn-focus-width !default;\n$input-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$input-placeholder-color: $gray-600 !default;\n$input-plaintext-color: $body-color !default;\n\n$input-height-border: $input-border-width * 2 !default;\n\n$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;\n$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;\n$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y / 2) !default;\n\n$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;\n$input-height-sm: add($input-line-height-sm * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;\n$input-height-lg: add($input-line-height-lg * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;\n\n$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$form-text-margin-top: .25rem !default;\n\n$form-check-input-gutter: 1.25rem !default;\n$form-check-input-margin-y: .3rem !default;\n$form-check-input-margin-x: .25rem !default;\n\n$form-check-inline-margin-x: .75rem !default;\n$form-check-inline-input-margin-x: .3125rem !default;\n\n$form-grid-gutter-width: 10px !default;\n$form-group-margin-bottom: 1rem !default;\n\n$input-group-addon-color: $input-color !default;\n$input-group-addon-bg: $gray-200 !default;\n$input-group-addon-border-color: $input-border-color !default;\n\n$custom-forms-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$custom-control-gutter: .5rem !default;\n$custom-control-spacer-x: 1rem !default;\n$custom-control-cursor: null !default;\n\n$custom-control-indicator-size: 1rem !default;\n$custom-control-indicator-bg: $input-bg !default;\n\n$custom-control-indicator-bg-size: 50% 50% !default;\n$custom-control-indicator-box-shadow: $input-box-shadow !default;\n$custom-control-indicator-border-color: $gray-500 !default;\n$custom-control-indicator-border-width: $input-border-width !default;\n\n$custom-control-label-color: null !default;\n\n$custom-control-indicator-disabled-bg: $input-disabled-bg !default;\n$custom-control-label-disabled-color: $gray-600 !default;\n\n$custom-control-indicator-checked-color: $component-active-color !default;\n$custom-control-indicator-checked-bg: $component-active-bg !default;\n$custom-control-indicator-checked-disabled-bg: rgba(theme-color(\"primary\"), .5) !default;\n$custom-control-indicator-checked-box-shadow: none !default;\n$custom-control-indicator-checked-border-color: $custom-control-indicator-checked-bg !default;\n\n$custom-control-indicator-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-control-indicator-focus-border-color: $input-focus-border-color !default;\n\n$custom-control-indicator-active-color: $component-active-color !default;\n$custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-control-indicator-active-box-shadow: none !default;\n$custom-control-indicator-active-border-color: $custom-control-indicator-active-bg !default;\n\n$custom-checkbox-indicator-border-radius: $border-radius !default;\n$custom-checkbox-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default;\n$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default;\n$custom-checkbox-indicator-icon-indeterminate: url(\"data:image/svg+xml,\") !default;\n$custom-checkbox-indicator-indeterminate-box-shadow: none !default;\n$custom-checkbox-indicator-indeterminate-border-color: $custom-checkbox-indicator-indeterminate-bg !default;\n\n$custom-radio-indicator-border-radius: 50% !default;\n$custom-radio-indicator-icon-checked: url(\"data:image/svg+xml,\") !default;\n\n$custom-switch-width: $custom-control-indicator-size * 1.75 !default;\n$custom-switch-indicator-border-radius: $custom-control-indicator-size / 2 !default;\n$custom-switch-indicator-size: subtract($custom-control-indicator-size, $custom-control-indicator-border-width * 4) !default;\n\n$custom-select-padding-y: $input-padding-y !default;\n$custom-select-padding-x: $input-padding-x !default;\n$custom-select-font-family: $input-font-family !default;\n$custom-select-font-size: $input-font-size !default;\n$custom-select-height: $input-height !default;\n$custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator\n$custom-select-font-weight: $input-font-weight !default;\n$custom-select-line-height: $input-line-height !default;\n$custom-select-color: $input-color !default;\n$custom-select-disabled-color: $gray-600 !default;\n$custom-select-bg: $input-bg !default;\n$custom-select-disabled-bg: $gray-200 !default;\n$custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions\n$custom-select-indicator-color: $gray-800 !default;\n$custom-select-indicator: url(\"data:image/svg+xml,\") !default;\n$custom-select-background: escape-svg($custom-select-indicator) no-repeat right $custom-select-padding-x center / $custom-select-bg-size !default; // Used so we can have multiple background elements (e.g., arrow and feedback icon)\n\n$custom-select-feedback-icon-padding-right: add(1em * .75, (2 * $custom-select-padding-y * .75) + $custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-position: center right ($custom-select-padding-x + $custom-select-indicator-padding) !default;\n$custom-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;\n\n$custom-select-border-width: $input-border-width !default;\n$custom-select-border-color: $input-border-color !default;\n$custom-select-border-radius: $border-radius !default;\n$custom-select-box-shadow: inset 0 1px 2px rgba($black, .075) !default;\n\n$custom-select-focus-border-color: $input-focus-border-color !default;\n$custom-select-focus-width: $input-focus-width !default;\n$custom-select-focus-box-shadow: 0 0 0 $custom-select-focus-width $input-btn-focus-color !default;\n\n$custom-select-padding-y-sm: $input-padding-y-sm !default;\n$custom-select-padding-x-sm: $input-padding-x-sm !default;\n$custom-select-font-size-sm: $input-font-size-sm !default;\n$custom-select-height-sm: $input-height-sm !default;\n\n$custom-select-padding-y-lg: $input-padding-y-lg !default;\n$custom-select-padding-x-lg: $input-padding-x-lg !default;\n$custom-select-font-size-lg: $input-font-size-lg !default;\n$custom-select-height-lg: $input-height-lg !default;\n\n$custom-range-track-width: 100% !default;\n$custom-range-track-height: .5rem !default;\n$custom-range-track-cursor: pointer !default;\n$custom-range-track-bg: $gray-300 !default;\n$custom-range-track-border-radius: 1rem !default;\n$custom-range-track-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default;\n\n$custom-range-thumb-width: 1rem !default;\n$custom-range-thumb-height: $custom-range-thumb-width !default;\n$custom-range-thumb-bg: $component-active-bg !default;\n$custom-range-thumb-border: 0 !default;\n$custom-range-thumb-border-radius: 1rem !default;\n$custom-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;\n$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;\n$custom-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in IE/Edge\n$custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default;\n$custom-range-thumb-disabled-bg: $gray-500 !default;\n\n$custom-file-height: $input-height !default;\n$custom-file-height-inner: $input-height-inner !default;\n$custom-file-focus-border-color: $input-focus-border-color !default;\n$custom-file-focus-box-shadow: $input-focus-box-shadow !default;\n$custom-file-disabled-bg: $input-disabled-bg !default;\n\n$custom-file-padding-y: $input-padding-y !default;\n$custom-file-padding-x: $input-padding-x !default;\n$custom-file-line-height: $input-line-height !default;\n$custom-file-font-family: $input-font-family !default;\n$custom-file-font-weight: $input-font-weight !default;\n$custom-file-color: $input-color !default;\n$custom-file-bg: $input-bg !default;\n$custom-file-border-width: $input-border-width !default;\n$custom-file-border-color: $input-border-color !default;\n$custom-file-border-radius: $input-border-radius !default;\n$custom-file-box-shadow: $input-box-shadow !default;\n$custom-file-button-color: $custom-file-color !default;\n$custom-file-button-bg: $input-group-addon-bg !default;\n$custom-file-text: (\n en: \"Browse\"\n) !default;\n\n\n// Form validation\n\n$form-feedback-margin-top: $form-text-margin-top !default;\n$form-feedback-font-size: $small-font-size !default;\n$form-feedback-valid-color: theme-color(\"success\") !default;\n$form-feedback-invalid-color: theme-color(\"danger\") !default;\n\n$form-feedback-icon-valid-color: $form-feedback-valid-color !default;\n$form-feedback-icon-valid: url(\"data:image/svg+xml,\") !default;\n$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;\n$form-feedback-icon-invalid: url(\"data:image/svg+xml,\") !default;\n\n$form-validation-states: () !default;\n// stylelint-disable-next-line scss/dollar-variable-default\n$form-validation-states: map-merge(\n (\n \"valid\": (\n \"color\": $form-feedback-valid-color,\n \"icon\": $form-feedback-icon-valid\n ),\n \"invalid\": (\n \"color\": $form-feedback-invalid-color,\n \"icon\": $form-feedback-icon-invalid\n ),\n ),\n $form-validation-states\n);\n\n// Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n\n$zindex-dropdown: 1000 !default;\n$zindex-sticky: 1020 !default;\n$zindex-fixed: 1030 !default;\n$zindex-modal-backdrop: 1040 !default;\n$zindex-modal: 1050 !default;\n$zindex-popover: 1060 !default;\n$zindex-tooltip: 1070 !default;\n\n\n// Navs\n\n$nav-link-padding-y: .5rem !default;\n$nav-link-padding-x: 1rem !default;\n$nav-link-disabled-color: $gray-600 !default;\n\n$nav-tabs-border-color: $gray-300 !default;\n$nav-tabs-border-width: $border-width !default;\n$nav-tabs-border-radius: $border-radius !default;\n$nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default;\n$nav-tabs-link-active-color: $gray-700 !default;\n$nav-tabs-link-active-bg: $body-bg !default;\n$nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default;\n\n$nav-pills-border-radius: $border-radius !default;\n$nav-pills-link-active-color: $component-active-color !default;\n$nav-pills-link-active-bg: $component-active-bg !default;\n\n$nav-divider-color: $gray-200 !default;\n$nav-divider-margin-y: $spacer / 2 !default;\n\n\n// Navbar\n\n$navbar-padding-y: $spacer / 2 !default;\n$navbar-padding-x: $spacer !default;\n\n$navbar-nav-link-padding-x: .5rem !default;\n\n$navbar-brand-font-size: $font-size-lg !default;\n// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link\n$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;\n$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;\n$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default;\n\n$navbar-toggler-padding-y: .25rem !default;\n$navbar-toggler-padding-x: .75rem !default;\n$navbar-toggler-font-size: $font-size-lg !default;\n$navbar-toggler-border-radius: $btn-border-radius !default;\n\n$navbar-dark-color: rgba($white, .5) !default;\n$navbar-dark-hover-color: rgba($white, .75) !default;\n$navbar-dark-active-color: $white !default;\n$navbar-dark-disabled-color: rgba($white, .25) !default;\n$navbar-dark-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-dark-toggler-border-color: rgba($white, .1) !default;\n\n$navbar-light-color: rgba($black, .5) !default;\n$navbar-light-hover-color: rgba($black, .7) !default;\n$navbar-light-active-color: rgba($black, .9) !default;\n$navbar-light-disabled-color: rgba($black, .3) !default;\n$navbar-light-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-light-toggler-border-color: rgba($black, .1) !default;\n\n$navbar-light-brand-color: $navbar-light-active-color !default;\n$navbar-light-brand-hover-color: $navbar-light-active-color !default;\n$navbar-dark-brand-color: $navbar-dark-active-color !default;\n$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;\n\n\n// Dropdowns\n//\n// Dropdown menu container and contents.\n\n$dropdown-min-width: 10rem !default;\n$dropdown-padding-y: .5rem !default;\n$dropdown-spacer: .125rem !default;\n$dropdown-font-size: $font-size-base !default;\n$dropdown-color: $body-color !default;\n$dropdown-bg: $white !default;\n$dropdown-border-color: rgba($black, .15) !default;\n$dropdown-border-radius: $border-radius !default;\n$dropdown-border-width: $border-width !default;\n$dropdown-inner-border-radius: subtract($dropdown-border-radius, $dropdown-border-width) !default;\n$dropdown-divider-bg: $gray-200 !default;\n$dropdown-divider-margin-y: $nav-divider-margin-y !default;\n$dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default;\n\n$dropdown-link-color: $gray-900 !default;\n$dropdown-link-hover-color: darken($gray-900, 5%) !default;\n$dropdown-link-hover-bg: $gray-100 !default;\n\n$dropdown-link-active-color: $component-active-color !default;\n$dropdown-link-active-bg: $component-active-bg !default;\n\n$dropdown-link-disabled-color: $gray-600 !default;\n\n$dropdown-item-padding-y: .25rem !default;\n$dropdown-item-padding-x: 1.5rem !default;\n\n$dropdown-header-color: $gray-600 !default;\n\n\n// Pagination\n\n$pagination-padding-y: .5rem !default;\n$pagination-padding-x: .75rem !default;\n$pagination-padding-y-sm: .25rem !default;\n$pagination-padding-x-sm: .5rem !default;\n$pagination-padding-y-lg: .75rem !default;\n$pagination-padding-x-lg: 1.5rem !default;\n$pagination-line-height: 1.25 !default;\n\n$pagination-color: $link-color !default;\n$pagination-bg: $white !default;\n$pagination-border-width: $border-width !default;\n$pagination-border-color: $gray-300 !default;\n\n$pagination-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$pagination-focus-outline: 0 !default;\n\n$pagination-hover-color: $link-hover-color !default;\n$pagination-hover-bg: $gray-200 !default;\n$pagination-hover-border-color: $gray-300 !default;\n\n$pagination-active-color: $component-active-color !default;\n$pagination-active-bg: $component-active-bg !default;\n$pagination-active-border-color: $pagination-active-bg !default;\n\n$pagination-disabled-color: $gray-600 !default;\n$pagination-disabled-bg: $white !default;\n$pagination-disabled-border-color: $gray-300 !default;\n\n\n// Jumbotron\n\n$jumbotron-padding: 2rem !default;\n$jumbotron-color: null !default;\n$jumbotron-bg: $gray-200 !default;\n\n\n// Cards\n\n$card-spacer-y: .75rem !default;\n$card-spacer-x: 1.25rem !default;\n$card-border-width: $border-width !default;\n$card-border-radius: $border-radius !default;\n$card-border-color: rgba($black, .125) !default;\n$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;\n$card-cap-bg: rgba($black, .03) !default;\n$card-cap-color: null !default;\n$card-height: null !default;\n$card-color: null !default;\n$card-bg: $white !default;\n\n$card-img-overlay-padding: 1.25rem !default;\n\n$card-group-margin: $grid-gutter-width / 2 !default;\n$card-deck-margin: $card-group-margin !default;\n\n$card-columns-count: 3 !default;\n$card-columns-gap: 1.25rem !default;\n$card-columns-margin: $card-spacer-y !default;\n\n\n// Tooltips\n\n$tooltip-font-size: $font-size-sm !default;\n$tooltip-max-width: 200px !default;\n$tooltip-color: $white !default;\n$tooltip-bg: $black !default;\n$tooltip-border-radius: $border-radius !default;\n$tooltip-opacity: .9 !default;\n$tooltip-padding-y: .25rem !default;\n$tooltip-padding-x: .5rem !default;\n$tooltip-margin: 0 !default;\n\n$tooltip-arrow-width: .8rem !default;\n$tooltip-arrow-height: .4rem !default;\n$tooltip-arrow-color: $tooltip-bg !default;\n\n// Form tooltips must come after regular tooltips\n$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;\n$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;\n$form-feedback-tooltip-font-size: $tooltip-font-size !default;\n$form-feedback-tooltip-line-height: $line-height-base !default;\n$form-feedback-tooltip-opacity: $tooltip-opacity !default;\n$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;\n\n\n// Popovers\n\n$popover-font-size: $font-size-sm !default;\n$popover-bg: $white !default;\n$popover-max-width: 276px !default;\n$popover-border-width: $border-width !default;\n$popover-border-color: rgba($black, .2) !default;\n$popover-border-radius: $border-radius-lg !default;\n$popover-inner-border-radius: subtract($popover-border-radius, $popover-border-width) !default;\n$popover-box-shadow: 0 .25rem .5rem rgba($black, .2) !default;\n\n$popover-header-bg: darken($popover-bg, 3%) !default;\n$popover-header-color: $headings-color !default;\n$popover-header-padding-y: .5rem !default;\n$popover-header-padding-x: .75rem !default;\n\n$popover-body-color: $body-color !default;\n$popover-body-padding-y: $popover-header-padding-y !default;\n$popover-body-padding-x: $popover-header-padding-x !default;\n\n$popover-arrow-width: 1rem !default;\n$popover-arrow-height: .5rem !default;\n$popover-arrow-color: $popover-bg !default;\n\n$popover-arrow-outer-color: fade-in($popover-border-color, .05) !default;\n\n\n// Toasts\n\n$toast-max-width: 350px !default;\n$toast-padding-x: .75rem !default;\n$toast-padding-y: .25rem !default;\n$toast-font-size: .875rem !default;\n$toast-color: null !default;\n$toast-background-color: rgba($white, .85) !default;\n$toast-border-width: 1px !default;\n$toast-border-color: rgba(0, 0, 0, .1) !default;\n$toast-border-radius: .25rem !default;\n$toast-box-shadow: 0 .25rem .75rem rgba($black, .1) !default;\n\n$toast-header-color: $gray-600 !default;\n$toast-header-background-color: rgba($white, .85) !default;\n$toast-header-border-color: rgba(0, 0, 0, .05) !default;\n\n\n// Badges\n\n$badge-font-size: 75% !default;\n$badge-font-weight: $font-weight-bold !default;\n$badge-padding-y: .25em !default;\n$badge-padding-x: .4em !default;\n$badge-border-radius: $border-radius !default;\n\n$badge-transition: $btn-transition !default;\n$badge-focus-width: $input-btn-focus-width !default;\n\n$badge-pill-padding-x: .6em !default;\n// Use a higher than normal value to ensure completely rounded edges when\n// customizing padding or font-size on labels.\n$badge-pill-border-radius: 10rem !default;\n\n\n// Modals\n\n// Padding applied to the modal body\n$modal-inner-padding: 1rem !default;\n\n// Margin between elements in footer, must be lower than or equal to 2 * $modal-inner-padding\n$modal-footer-margin-between: .5rem !default;\n\n$modal-dialog-margin: .5rem !default;\n$modal-dialog-margin-y-sm-up: 1.75rem !default;\n\n$modal-title-line-height: $line-height-base !default;\n\n$modal-content-color: null !default;\n$modal-content-bg: $white !default;\n$modal-content-border-color: rgba($black, .2) !default;\n$modal-content-border-width: $border-width !default;\n$modal-content-border-radius: $border-radius-lg !default;\n$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;\n$modal-content-box-shadow-xs: 0 .25rem .5rem rgba($black, .5) !default;\n$modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default;\n\n$modal-backdrop-bg: $black !default;\n$modal-backdrop-opacity: .5 !default;\n$modal-header-border-color: $border-color !default;\n$modal-footer-border-color: $modal-header-border-color !default;\n$modal-header-border-width: $modal-content-border-width !default;\n$modal-footer-border-width: $modal-header-border-width !default;\n$modal-header-padding-y: 1rem !default;\n$modal-header-padding-x: 1rem !default;\n$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility\n\n$modal-xl: 1140px !default;\n$modal-lg: 800px !default;\n$modal-md: 500px !default;\n$modal-sm: 300px !default;\n\n$modal-fade-transform: translate(0, -50px) !default;\n$modal-show-transform: none !default;\n$modal-transition: transform .3s ease-out !default;\n$modal-scale-transform: scale(1.02) !default;\n\n\n// Alerts\n//\n// Define alert colors, border radius, and padding.\n\n$alert-padding-y: .75rem !default;\n$alert-padding-x: 1.25rem !default;\n$alert-margin-bottom: 1rem !default;\n$alert-border-radius: $border-radius !default;\n$alert-link-font-weight: $font-weight-bold !default;\n$alert-border-width: $border-width !default;\n\n$alert-bg-level: -10 !default;\n$alert-border-level: -9 !default;\n$alert-color-level: 6 !default;\n\n\n// Progress bars\n\n$progress-height: 1rem !default;\n$progress-font-size: $font-size-base * .75 !default;\n$progress-bg: $gray-200 !default;\n$progress-border-radius: $border-radius !default;\n$progress-box-shadow: inset 0 .1rem .1rem rgba($black, .1) !default;\n$progress-bar-color: $white !default;\n$progress-bar-bg: theme-color(\"primary\") !default;\n$progress-bar-animation-timing: 1s linear infinite !default;\n$progress-bar-transition: width .6s ease !default;\n\n\n// List group\n\n$list-group-color: null !default;\n$list-group-bg: $white !default;\n$list-group-border-color: rgba($black, .125) !default;\n$list-group-border-width: $border-width !default;\n$list-group-border-radius: $border-radius !default;\n\n$list-group-item-padding-y: .75rem !default;\n$list-group-item-padding-x: 1.25rem !default;\n\n$list-group-hover-bg: $gray-100 !default;\n$list-group-active-color: $component-active-color !default;\n$list-group-active-bg: $component-active-bg !default;\n$list-group-active-border-color: $list-group-active-bg !default;\n\n$list-group-disabled-color: $gray-600 !default;\n$list-group-disabled-bg: $list-group-bg !default;\n\n$list-group-action-color: $gray-700 !default;\n$list-group-action-hover-color: $list-group-action-color !default;\n\n$list-group-action-active-color: $body-color !default;\n$list-group-action-active-bg: $gray-200 !default;\n\n\n// Image thumbnails\n\n$thumbnail-padding: .25rem !default;\n$thumbnail-bg: $body-bg !default;\n$thumbnail-border-width: $border-width !default;\n$thumbnail-border-color: $gray-300 !default;\n$thumbnail-border-radius: $border-radius !default;\n$thumbnail-box-shadow: 0 1px 2px rgba($black, .075) !default;\n\n\n// Figures\n\n$figure-caption-font-size: 90% !default;\n$figure-caption-color: $gray-600 !default;\n\n\n// Breadcrumbs\n\n$breadcrumb-font-size: null !default;\n\n$breadcrumb-padding-y: .75rem !default;\n$breadcrumb-padding-x: 1rem !default;\n$breadcrumb-item-padding: .5rem !default;\n\n$breadcrumb-margin-bottom: 1rem !default;\n\n$breadcrumb-bg: $gray-200 !default;\n$breadcrumb-divider-color: $gray-600 !default;\n$breadcrumb-active-color: $gray-600 !default;\n$breadcrumb-divider: quote(\"/\") !default;\n\n$breadcrumb-border-radius: $border-radius !default;\n\n\n// Carousel\n\n$carousel-control-color: $white !default;\n$carousel-control-width: 15% !default;\n$carousel-control-opacity: .5 !default;\n$carousel-control-hover-opacity: .9 !default;\n$carousel-control-transition: opacity .15s ease !default;\n\n$carousel-indicator-width: 30px !default;\n$carousel-indicator-height: 3px !default;\n$carousel-indicator-hit-area-height: 10px !default;\n$carousel-indicator-spacer: 3px !default;\n$carousel-indicator-active-bg: $white !default;\n$carousel-indicator-transition: opacity .6s ease !default;\n\n$carousel-caption-width: 70% !default;\n$carousel-caption-color: $white !default;\n\n$carousel-control-icon-width: 20px !default;\n\n$carousel-control-prev-icon-bg: url(\"data:image/svg+xml,\") !default;\n$carousel-control-next-icon-bg: url(\"data:image/svg+xml,\") !default;\n\n$carousel-transition-duration: .6s !default;\n$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)\n\n\n// Spinners\n\n$spinner-width: 2rem !default;\n$spinner-height: $spinner-width !default;\n$spinner-border-width: .25em !default;\n\n$spinner-width-sm: 1rem !default;\n$spinner-height-sm: $spinner-width-sm !default;\n$spinner-border-width-sm: .2em !default;\n\n\n// Close\n\n$close-font-size: $font-size-base * 1.5 !default;\n$close-font-weight: $font-weight-bold !default;\n$close-color: $black !default;\n$close-text-shadow: 0 1px 0 $white !default;\n\n\n// Code\n\n$code-font-size: 87.5% !default;\n$code-color: $pink !default;\n\n$kbd-padding-y: .2rem !default;\n$kbd-padding-x: .4rem !default;\n$kbd-font-size: $code-font-size !default;\n$kbd-color: $white !default;\n$kbd-bg: $gray-900 !default;\n\n$pre-color: $gray-900 !default;\n$pre-scrollable-max-height: 340px !default;\n\n\n// Utilities\n\n$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex !default;\n$overflows: auto, hidden !default;\n$positions: static, relative, absolute, fixed, sticky !default;\n\n\n// Printing\n\n$print-page-size: a3 !default;\n$print-body-min-width: map-get($grid-breakpoints, \"lg\") !default;\n","// stylelint-disable property-blacklist, scss/dollar-variable-default\n\n// SCSS RFS mixin\n//\n// Automated font-resizing\n//\n// See https://github.com/twbs/rfs\n\n// Configuration\n\n// Base font size\n$rfs-base-font-size: 1.25rem !default;\n$rfs-font-size-unit: rem !default;\n\n// Breakpoint at where font-size starts decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n// Resize font-size based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != \"number\" or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-responsive-font-sizes to false\n$enable-responsive-font-sizes: true !default;\n\n// Cache $rfs-base-font-size unit\n$rfs-base-font-size-unit: unit($rfs-base-font-size);\n\n// Remove px-unit from $rfs-base-font-size for calculations\n@if $rfs-base-font-size-unit == \"px\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1);\n}\n@else if $rfs-base-font-size-unit == \"rem\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1 / $rfs-rem-value);\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == \"px\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == \"rem\" or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value);\n}\n\n// Responsive font-size mixin\n@mixin rfs($fs, $important: false) {\n // Cache $fs unit\n $fs-unit: if(type-of($fs) == \"number\", unit($fs), false);\n\n // Add !important suffix if needed\n $rfs-suffix: if($important, \" !important\", \"\");\n\n // If $fs isn't a number (like inherit) or $fs has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $fs-unit or $fs-unit != \"\" and $fs-unit != \"px\" and $fs-unit != \"rem\" or $fs == 0 {\n font-size: #{$fs}#{$rfs-suffix};\n }\n @else {\n // Variables for storing static and fluid rescaling\n $rfs-static: null;\n $rfs-fluid: null;\n\n // Remove px-unit from $fs for calculations\n @if $fs-unit == \"px\" {\n $fs: $fs / ($fs * 0 + 1);\n }\n @else if $fs-unit == \"rem\" {\n $fs: $fs / ($fs * 0 + 1 / $rfs-rem-value);\n }\n\n // Set default font-size\n @if $rfs-font-size-unit == rem {\n $rfs-static: #{$fs / $rfs-rem-value}rem#{$rfs-suffix};\n }\n @else if $rfs-font-size-unit == px {\n $rfs-static: #{$fs}px#{$rfs-suffix};\n }\n @else {\n @error \"`#{$rfs-font-size-unit}` is not a valid unit for $rfs-font-size-unit. Use `px` or `rem`.\";\n }\n\n // Only add media query if font-size is bigger as the minimum font-size\n // If $rfs-factor == 1, no rescaling will take place\n @if $fs > $rfs-base-font-size and $enable-responsive-font-sizes {\n $min-width: null;\n $variable-unit: null;\n\n // Calculate minimum font-size for given font-size\n $fs-min: $rfs-base-font-size + ($fs - $rfs-base-font-size) / $rfs-factor;\n\n // Calculate difference between given font-size and minimum font-size for given font-size\n $fs-diff: $fs - $fs-min;\n\n // Base font-size formatting\n // No need to check if the unit is valid, because we did that before\n $min-width: if($rfs-font-size-unit == rem, #{$fs-min / $rfs-rem-value}rem, #{$fs-min}px);\n\n // If two-dimensional, use smallest of screen width and height\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{$fs-diff * 100 / $rfs-breakpoint}#{$variable-unit};\n\n // Set the calculated font-size.\n $rfs-fluid: calc(#{$min-width} + #{$variable-width}) #{$rfs-suffix};\n }\n\n // Rendering\n @if $rfs-fluid == null {\n // Only render static font-size if no fluid font-size is available\n font-size: $rfs-static;\n }\n @else {\n $mq-value: null;\n\n // RFS breakpoint formatting\n @if $rfs-breakpoint-unit == em or $rfs-breakpoint-unit == rem {\n $mq-value: #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit};\n }\n @else if $rfs-breakpoint-unit == px {\n $mq-value: #{$rfs-breakpoint}px;\n }\n @else {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n }\n\n @if $rfs-class == \"disable\" {\n // Adding an extra class increases specificity,\n // which prevents the media query to override the font size\n &,\n .disable-responsive-font-size &,\n &.disable-responsive-font-size {\n font-size: $rfs-static;\n }\n }\n @else {\n font-size: $rfs-static;\n }\n\n @if $rfs-two-dimensional {\n @media (max-width: #{$mq-value}), (max-height: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n @else {\n @media (max-width: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n }\n }\n}\n\n// The font-size & responsive-font-size mixin uses RFS to rescale font sizes\n@mixin font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n\n@mixin responsive-font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover() {\n &:hover { @content; }\n}\n\n@mixin hover-focus() {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus() {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active() {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n","// stylelint-disable declaration-no-important, selector-list-comma-newline-after\n\n//\n// Headings\n//\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1, .h1 { @include font-size($h1-font-size); }\nh2, .h2 { @include font-size($h2-font-size); }\nh3, .h3 { @include font-size($h3-font-size); }\nh4, .h4 { @include font-size($h4-font-size); }\nh5, .h5 { @include font-size($h5-font-size); }\nh6, .h6 { @include font-size($h6-font-size); }\n\n.lead {\n @include font-size($lead-font-size);\n font-weight: $lead-font-weight;\n}\n\n// Type display classes\n.display-1 {\n @include font-size($display1-size);\n font-weight: $display1-weight;\n line-height: $display-line-height;\n}\n.display-2 {\n @include font-size($display2-size);\n font-weight: $display2-weight;\n line-height: $display-line-height;\n}\n.display-3 {\n @include font-size($display3-size);\n font-weight: $display3-weight;\n line-height: $display-line-height;\n}\n.display-4 {\n @include font-size($display4-size);\n font-weight: $display4-weight;\n line-height: $display-line-height;\n}\n\n\n//\n// Horizontal rules\n//\n\nhr {\n margin-top: $hr-margin-y;\n margin-bottom: $hr-margin-y;\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n}\n\n\n//\n// Emphasis\n//\n\nsmall,\n.small {\n @include font-size($small-font-size);\n font-weight: $font-weight-normal;\n}\n\nmark,\n.mark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n//\n// Lists\n//\n\n.list-unstyled {\n @include list-unstyled();\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n @include list-unstyled();\n}\n.list-inline-item {\n display: inline-block;\n\n &:not(:last-child) {\n margin-right: $list-inline-padding;\n }\n}\n\n\n//\n// Misc\n//\n\n// Builds on `abbr`\n.initialism {\n @include font-size(90%);\n text-transform: uppercase;\n}\n\n// Blockquotes\n.blockquote {\n margin-bottom: $spacer;\n @include font-size($blockquote-font-size);\n}\n\n.blockquote-footer {\n display: block;\n @include font-size($blockquote-small-font-size);\n color: $blockquote-small-color;\n\n &::before {\n content: \"\\2014\\00A0\"; // em dash, nbsp\n }\n}\n","// Lists\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n@mixin list-unstyled() {\n padding-left: 0;\n list-style: none;\n}\n","// Responsive images (ensure images don't scale beyond their parents)\n//\n// This is purposefully opt-in via an explicit class rather than being the default for all ``s.\n// We previously tried the \"images are responsive by default\" approach in Bootstrap v2,\n// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)\n// which weren't expecting the images within themselves to be involuntarily resized.\n// See also https://github.com/twbs/bootstrap/issues/18178\n.img-fluid {\n @include img-fluid();\n}\n\n\n// Image thumbnails\n.img-thumbnail {\n padding: $thumbnail-padding;\n background-color: $thumbnail-bg;\n border: $thumbnail-border-width solid $thumbnail-border-color;\n @include border-radius($thumbnail-border-radius);\n @include box-shadow($thumbnail-box-shadow);\n\n // Keep them at most 100% wide\n @include img-fluid();\n}\n\n//\n// Figures\n//\n\n.figure {\n // Ensures the caption's text aligns with the image.\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: $spacer / 2;\n line-height: 1;\n}\n\n.figure-caption {\n @include font-size($figure-caption-font-size);\n color: $figure-caption-color;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n@mixin img-fluid() {\n // Part 1: Set a maximum relative to the parent\n max-width: 100%;\n // Part 2: Override the height to auto, otherwise images will be stretched\n // when setting a width and height attribute on the img element.\n height: auto;\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size.\n\n@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {\n background-image: url($file-1x);\n\n // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,\n // but doesn't convert dppx=>dpi.\n // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.\n // Compatibility info: https://caniuse.com/#feat=css-media-resolution\n @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx\n only screen and (min-resolution: 2dppx) { // Standardized\n background-image: url($file-2x);\n background-size: $width-1x $height-1x;\n }\n @include deprecate(\"`img-retina()`\", \"v4.3.0\", \"v5\");\n}\n","// stylelint-disable property-blacklist\n// Single side border-radius\n\n@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) {\n @if $enable-rounded {\n border-radius: $radius;\n }\n @else if $fallback-border-radius != false {\n border-radius: $fallback-border-radius;\n }\n}\n\n@mixin border-top-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-top-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n }\n}\n\n@mixin border-top-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-right-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-left-radius($radius) {\n @if $enable-rounded {\n border-bottom-left-radius: $radius;\n }\n}\n","// Inline code\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n @include box-shadow($kbd-box-shadow);\n\n kbd {\n padding: 0;\n @include font-size(100%);\n font-weight: $nested-kbd-font-weight;\n @include box-shadow(none);\n }\n}\n\n// Blocks of code\npre {\n display: block;\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: $pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n // Single container class with breakpoint max-widths\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n\n // 100% wide container at all breakpoints\n .container-fluid {\n @include make-container();\n }\n\n // Responsive containers that are 100% wide until a breakpoint\n @each $breakpoint, $container-max-width in $container-max-widths {\n .container-#{$breakpoint} {\n @extend .container-fluid;\n }\n\n @include media-breakpoint-up($breakpoint, $grid-breakpoints) {\n %responsive-container-#{$breakpoint} {\n max-width: $container-max-width;\n }\n\n @each $name, $width in $grid-breakpoints {\n @if ($container-max-width > $width or $breakpoint == $name) {\n .container#{breakpoint-infix($name, $grid-breakpoints)} {\n @extend %responsive-container-#{$breakpoint};\n }\n }\n }\n }\n }\n}\n\n\n// Row\n//\n// Rows contain your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n }\n}\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","/// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-container($gutter: $grid-gutter-width) {\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row($gutter: $grid-gutter-width) {\n display: flex;\n flex-wrap: wrap;\n margin-right: -$gutter / 2;\n margin-left: -$gutter / 2;\n}\n\n@mixin make-col-ready($gutter: $grid-gutter-width) {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-auto() {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%; // Reset earlier grid tiers\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\n}\n\n// Row columns\n//\n// Specify on a parent element(e.g., .row) to force immediate children into NN\n// numberof columns. Supports wrapping to new lines, but does not do a Masonry\n// style grid.\n@mixin row-cols($count) {\n & > * {\n flex: 0 0 100% / $count;\n max-width: 100% / $count;\n }\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n != null and $n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .02, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $max: breakpoint-max($name, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($name, $breakpoints) {\n @content;\n }\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n\n @for $i from 1 through $grid-row-columns {\n .row-cols#{$infix}-#{$i} {\n @include row-cols($i);\n }\n }\n\n .col#{$infix}-auto {\n @include make-col-auto();\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n }\n}\n","//\n// Basic Bootstrap table\n//\n\n.table {\n width: 100%;\n margin-bottom: $spacer;\n color: $table-color;\n background-color: $table-bg; // Reset for nesting within parents with `background-color`.\n\n th,\n td {\n padding: $table-cell-padding;\n vertical-align: top;\n border-top: $table-border-width solid $table-border-color;\n }\n\n thead th {\n vertical-align: bottom;\n border-bottom: (2 * $table-border-width) solid $table-border-color;\n }\n\n tbody + tbody {\n border-top: (2 * $table-border-width) solid $table-border-color;\n }\n}\n\n\n//\n// Condensed table w/ half padding\n//\n\n.table-sm {\n th,\n td {\n padding: $table-cell-padding-sm;\n }\n}\n\n\n// Border versions\n//\n// Add or remove borders all around the table and between all the columns.\n\n.table-bordered {\n border: $table-border-width solid $table-border-color;\n\n th,\n td {\n border: $table-border-width solid $table-border-color;\n }\n\n thead {\n th,\n td {\n border-bottom-width: 2 * $table-border-width;\n }\n }\n}\n\n.table-borderless {\n th,\n td,\n thead th,\n tbody + tbody {\n border: 0;\n }\n}\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n tbody tr:nth-of-type(#{$table-striped-order}) {\n background-color: $table-accent-bg;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n tbody tr {\n @include hover() {\n color: $table-hover-color;\n background-color: $table-hover-bg;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n@each $color, $value in $theme-colors {\n @include table-row-variant($color, theme-color-level($color, $table-bg-level), theme-color-level($color, $table-border-level));\n}\n\n@include table-row-variant(active, $table-active-bg);\n\n\n// Dark styles\n//\n// Same table markup, but inverted color scheme: dark background and light text.\n\n// stylelint-disable-next-line no-duplicate-selectors\n.table {\n .thead-dark {\n th {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n border-color: $table-dark-border-color;\n }\n }\n\n .thead-light {\n th {\n color: $table-head-color;\n background-color: $table-head-bg;\n border-color: $table-border-color;\n }\n }\n}\n\n.table-dark {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n\n th,\n td,\n thead th {\n border-color: $table-dark-border-color;\n }\n\n &.table-bordered {\n border: 0;\n }\n\n &.table-striped {\n tbody tr:nth-of-type(#{$table-striped-order}) {\n background-color: $table-dark-accent-bg;\n }\n }\n\n &.table-hover {\n tbody tr {\n @include hover() {\n color: $table-dark-hover-color;\n background-color: $table-dark-hover-bg;\n }\n }\n }\n}\n\n\n// Responsive tables\n//\n// Generate series of `.table-responsive-*` classes for configuring the screen\n// size of where your table will overflow.\n\n.table-responsive {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $next: breakpoint-next($breakpoint, $grid-breakpoints);\n $infix: breakpoint-infix($next, $grid-breakpoints);\n\n {$infix} {\n @include media-breakpoint-down($breakpoint) {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n\n // Prevent double border on horizontal scroll due to use of `display: block;`\n > .table-bordered {\n border: 0;\n }\n }\n }\n }\n}\n","// Tables\n\n@mixin table-row-variant($state, $background, $border: null) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table-#{$state} {\n &,\n > th,\n > td {\n background-color: $background;\n }\n\n @if $border != null {\n th,\n td,\n thead th,\n tbody + tbody {\n border-color: $border;\n }\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover {\n $hover-background: darken($background, 5%);\n\n .table-#{$state} {\n @include hover() {\n background-color: $hover-background;\n\n > td,\n > th {\n background-color: $hover-background;\n }\n }\n }\n }\n}\n","// Bootstrap functions\n//\n// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.\n\n// Ascending\n// Used to evaluate Sass maps like our grid breakpoints.\n@mixin _assert-ascending($map, $map-name) {\n $prev-key: null;\n $prev-num: null;\n @each $key, $num in $map {\n @if $prev-num == null or unit($num) == \"%\" or unit($prev-num) == \"%\" {\n // Do nothing\n } @else if not comparable($prev-num, $num) {\n @warn \"Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !\";\n } @else if $prev-num >= $num {\n @warn \"Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !\";\n }\n $prev-key: $key;\n $prev-num: $num;\n }\n}\n\n// Starts at zero\n// Used to ensure the min-width of the lowest breakpoint starts at 0.\n@mixin _assert-starts-at-zero($map, $map-name: \"$grid-breakpoints\") {\n $values: map-values($map);\n $first-value: nth($values, 1);\n @if $first-value != 0 {\n @warn \"First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.\";\n }\n}\n\n// Replace `$search` with `$replace` in `$string`\n// Used on our SVG icon backgrounds for custom forms.\n//\n// @author Hugo Giraudel\n// @param {String} $string - Initial string\n// @param {String} $search - Substring to replace\n// @param {String} $replace ('') - New value\n// @return {String} - Updated string\n@function str-replace($string, $search, $replace: \"\") {\n $index: str-index($string, $search);\n\n @if $index {\n @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);\n }\n\n @return $string;\n}\n\n// See https://codepen.io/kevinweber/pen/dXWoRw\n@function escape-svg($string) {\n @if str-index($string, \"data:image/svg+xml\") {\n @each $char, $encoded in $escaped-characters {\n $string: str-replace($string, $char, $encoded);\n }\n }\n\n @return $string;\n}\n\n// Color contrast\n@function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light) {\n $r: red($color);\n $g: green($color);\n $b: blue($color);\n\n $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;\n\n @if ($yiq >= $yiq-contrasted-threshold) {\n @return $dark;\n } @else {\n @return $light;\n }\n}\n\n// Retrieve color Sass maps\n@function color($key: \"blue\") {\n @return map-get($colors, $key);\n}\n\n@function theme-color($key: \"primary\") {\n @return map-get($theme-colors, $key);\n}\n\n@function gray($key: \"100\") {\n @return map-get($grays, $key);\n}\n\n// Request a theme color level\n@function theme-color-level($color-name: \"primary\", $level: 0) {\n $color: theme-color($color-name);\n $color-base: if($level > 0, $black, $white);\n $level: abs($level);\n\n @return mix($color-base, $color, $level * $theme-color-interval);\n}\n\n// Return valid calc\n@function add($value1, $value2, $return-calc: true) {\n @if $value1 == null {\n @return $value2;\n }\n\n @if $value2 == null {\n @return $value1;\n }\n\n @if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {\n @return $value1 + $value2;\n }\n\n @return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + unquote(\" + \") + $value2);\n}\n\n@function subtract($value1, $value2, $return-calc: true) {\n @if $value1 == null and $value2 == null {\n @return null;\n }\n\n @if $value1 == null {\n @return -$value2;\n }\n\n @if $value2 == null {\n @return $value1;\n }\n\n @if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {\n @return $value1 - $value2;\n }\n\n @return if($return-calc == true, calc(#{$value1} - #{$value2}), $value1 + unquote(\" - \") + $value2);\n}\n","// stylelint-disable selector-no-qualifying-type\n\n//\n// Textual form controls\n//\n\n.form-control {\n display: block;\n width: 100%;\n height: $input-height;\n padding: $input-padding-y $input-padding-x;\n font-family: $input-font-family;\n @include font-size($input-font-size);\n font-weight: $input-font-weight;\n line-height: $input-line-height;\n color: $input-color;\n background-color: $input-bg;\n background-clip: padding-box;\n border: $input-border-width solid $input-border-color;\n\n // Note: This has no effect on