diff --git a/OpenIddict.sln b/OpenIddict.sln index 5f526033..a91cd59f 100644 --- a/OpenIddict.sln +++ b/OpenIddict.sln @@ -13,16 +13,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mvc.Client", "samples\Mvc.C EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mvc.Server", "samples\Mvc.Server\Mvc.Server.xproj", "{7CBEAFD2-E3D0-4424-9B78-E87AB52327A6}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Assets", "src\OpenIddict.Assets\OpenIddict.Assets.xproj", "{86293E11-DD31-4D54-BCAD-8788B5C9972F}" -EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.EntityFramework", "src\OpenIddict.EntityFramework\OpenIddict.EntityFramework.xproj", "{D2450929-ED0E-420D-B475-327924F9701C}" EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Core", "src\OpenIddict.Core\OpenIddict.Core.xproj", "{E60CF8CA-6313-4359-BE43-AFCBB927EA30}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Mvc", "src\OpenIddict.Mvc\OpenIddict.Mvc.xproj", "{7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}" -EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Security", "src\OpenIddict.Security\OpenIddict.Security.xproj", "{3744B1BC-3498-4958-B020-B2688A78B989}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,10 +35,6 @@ Global {7CBEAFD2-E3D0-4424-9B78-E87AB52327A6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7CBEAFD2-E3D0-4424-9B78-E87AB52327A6}.Release|Any CPU.ActiveCfg = Release|Any CPU {7CBEAFD2-E3D0-4424-9B78-E87AB52327A6}.Release|Any CPU.Build.0 = Release|Any CPU - {86293E11-DD31-4D54-BCAD-8788B5C9972F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86293E11-DD31-4D54-BCAD-8788B5C9972F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86293E11-DD31-4D54-BCAD-8788B5C9972F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86293E11-DD31-4D54-BCAD-8788B5C9972F}.Release|Any CPU.Build.0 = Release|Any CPU {D2450929-ED0E-420D-B475-327924F9701C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D2450929-ED0E-420D-B475-327924F9701C}.Debug|Any CPU.Build.0 = Debug|Any CPU {D2450929-ED0E-420D-B475-327924F9701C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -53,14 +43,6 @@ Global {E60CF8CA-6313-4359-BE43-AFCBB927EA30}.Debug|Any CPU.Build.0 = Debug|Any CPU {E60CF8CA-6313-4359-BE43-AFCBB927EA30}.Release|Any CPU.ActiveCfg = Release|Any CPU {E60CF8CA-6313-4359-BE43-AFCBB927EA30}.Release|Any CPU.Build.0 = Release|Any CPU - {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Release|Any CPU.Build.0 = Release|Any CPU - {3744B1BC-3498-4958-B020-B2688A78B989}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3744B1BC-3498-4958-B020-B2688A78B989}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3744B1BC-3498-4958-B020-B2688A78B989}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3744B1BC-3498-4958-B020-B2688A78B989}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -69,10 +51,7 @@ Global {80A8D6CE-C29A-4602-9844-D51FEF9C33C8} = {D544447C-D701-46BB-9A5B-C76C612A596B} {96B22EB9-771A-4DCA-B828-E6EA2774CF1B} = {F47D1283-0EE9-4728-8026-58405C29B786} {7CBEAFD2-E3D0-4424-9B78-E87AB52327A6} = {F47D1283-0EE9-4728-8026-58405C29B786} - {86293E11-DD31-4D54-BCAD-8788B5C9972F} = {D544447C-D701-46BB-9A5B-C76C612A596B} {D2450929-ED0E-420D-B475-327924F9701C} = {D544447C-D701-46BB-9A5B-C76C612A596B} {E60CF8CA-6313-4359-BE43-AFCBB927EA30} = {D544447C-D701-46BB-9A5B-C76C612A596B} - {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84} = {D544447C-D701-46BB-9A5B-C76C612A596B} - {3744B1BC-3498-4958-B020-B2688A78B989} = {D544447C-D701-46BB-9A5B-C76C612A596B} EndGlobalSection EndGlobal diff --git a/samples/Mvc.Server/Controllers/AuthorizationController.cs b/samples/Mvc.Server/Controllers/AuthorizationController.cs new file mode 100644 index 00000000..f86e4c7b --- /dev/null +++ b/samples/Mvc.Server/Controllers/AuthorizationController.cs @@ -0,0 +1,134 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using AspNet.Security.OpenIdConnect.Extensions; +using AspNet.Security.OpenIdConnect.Server; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Mvc.Server.Models; +using Mvc.Server.ViewModels.Authorization; +using OpenIddict; + +namespace Mvc.Server { + public class AuthorizationController : Controller { + private readonly OpenIddictApplicationManager> _applicationManager; + private readonly SignInManager _signInManager; + private readonly OpenIddictUserManager _userManager; + + public AuthorizationController( + OpenIddictApplicationManager> applicationManager, + SignInManager signInManager, + OpenIddictUserManager userManager) { + _applicationManager = applicationManager; + _signInManager = signInManager; + _userManager = userManager; + } + + [Authorize, HttpGet, Route("~/connect/authorize")] + public async Task Authorize() { + // Extract the authorization request from the ASP.NET environment. + var request = HttpContext.GetOpenIdConnectRequest(); + + // Retrieve the application details from the database. + var application = await _applicationManager.FindByClientIdAsync(request.ClientId); + if (application == null) { + return View("Error", new ErrorViewModel { + Error = OpenIdConnectConstants.Errors.InvalidClient, + ErrorDescription = "Details concerning the calling client application cannot be found in the database" + }); + } + + return View(new AuthorizeViewModel { + ApplicationName = application.DisplayName, + Parameters = request.Parameters, + Scope = request.Scope + }); + } + + [Authorize, HttpPost("~/connect/authorize/accept"), ValidateAntiForgeryToken] + public async Task Accept() { + // Extract the authorization request from the ASP.NET environment. + var request = HttpContext.GetOpenIdConnectRequest(); + + // Retrieve the profile of the logged in user. + var user = await _userManager.GetUserAsync(User); + if (user == null) { + return View("Error", new ErrorViewModel { + Error = OpenIdConnectConstants.Errors.ServerError, + ErrorDescription = "An internal error has occurred" + }); + } + + // Create a new ClaimsIdentity containing the claims that + // will be used to create an id_token, a token or a code. + var identity = await _userManager.CreateIdentityAsync(user, request.GetScopes()); + + // Create a new authentication ticket holding the user identity. + var ticket = new AuthenticationTicket( + new ClaimsPrincipal(identity), + new AuthenticationProperties(), + OpenIdConnectServerDefaults.AuthenticationScheme); + + ticket.SetResources(request.GetResources()); + ticket.SetScopes(request.GetScopes()); + + // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. + return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme); + } + + [Authorize, HttpPost("~/connect/authorize/deny"), ValidateAntiForgeryToken] + public IActionResult Deny() { + // Notify OpenIddict that the authorization grant has been denied by the resource owner + // to redirect the user agent to the client application using the appropriate response_mode. + return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme); + } + + [HttpGet("~/connect/logout")] + public IActionResult Logout() { + // Extract the authorization request from the ASP.NET environment. + var request = HttpContext.GetOpenIdConnectRequest(); + + return View(new LogoutViewModel { + Parameters = request.Parameters + }); + } + + [HttpPost("~/connect/logout"), ValidateAntiForgeryToken] + public async Task Logout(CancellationToken cancellationToken) { + // Ask ASP.NET Core Identity to delete the local and external cookies created + // when the user agent is redirected from the external identity provider + // after a successful authentication flow (e.g Google or Facebook). + await _signInManager.SignOutAsync(); + + // Returning a SignOutResult will ask OpenIddict to redirect the user agent + // to the post_logout_redirect_uri specified by the client application. + return SignOut(OpenIdConnectServerDefaults.AuthenticationScheme); + } + + [HttpGet, HttpPost, Route("~/connect/error")] + public IActionResult Error() { + var response = HttpContext.GetOpenIdConnectResponse(); + if (response == null) { + return View(new ErrorViewModel { + Error = OpenIdConnectConstants.Errors.InvalidRequest + }); + } + + return View(new ErrorViewModel { + Error = response.Error, + ErrorDescription = response.ErrorDescription + }); + } + } +} \ No newline at end of file diff --git a/samples/Mvc.Server/Startup.cs b/samples/Mvc.Server/Startup.cs index 99936144..c451e50d 100644 --- a/samples/Mvc.Server/Startup.cs +++ b/samples/Mvc.Server/Startup.cs @@ -33,23 +33,9 @@ namespace Mvc.Server { // Register the OpenIddict services, including the default Entity Framework stores. services.AddOpenIddict, ApplicationDbContext, Guid>() - // Register the HTML/CSS assets and MVC modules to handle the interactive flows. - // Note: these modules are not necessary when using your own authorization controller - // or when using non-interactive flows-only like the resource owner password credentials grant. - .AddAssets() - .AddMvc() - - // Register the NWebsec module. Note: you can replace the default Content Security Policy (CSP) - // by calling UseNWebsec with a custom delegate instead of using the parameterless extension. - // This can be useful to allow your HTML views to reference remote scripts/images/styles. - .AddNWebsec(options => options.DefaultSources(directive => directive.Self()) - .ImageSources(directive => directive.Self() - .CustomSources("*")) - .ScriptSources(directive => directive.Self() - .UnsafeInline() - .CustomSources("https://my.custom.url/")) - .StyleSources(directive => directive.Self() - .UnsafeInline())) + .SetAuthorizationEndpointPath("/connect/authorize") + .SetLogoutEndpointPath("/connect/logout") + .SetErrorHandlingPath("/connect/error") // During development, you can disable the HTTPS requirement. .DisableHttpsRequirement(); @@ -105,6 +91,20 @@ namespace Mvc.Server { // options.ClientSecret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd"; // }); + app.UseCsp(options => options.DefaultSources(directive => directive.Self()) + .ImageSources(directive => directive.Self() + .CustomSources("*")) + .ScriptSources(directive => directive.Self() + .UnsafeInline()) + .StyleSources(directive => directive.Self() + .UnsafeInline())); + + app.UseXContentTypeOptions(); + + app.UseXfo(options => options.Deny()); + + app.UseXXssProtection(options => options.EnabledWithBlockMode()); + app.UseIdentity(); app.UseGoogleAuthentication(new GoogleOptions { diff --git a/samples/Mvc.Server/ViewModels/Authorization/AuthorizeViewModel.cs b/samples/Mvc.Server/ViewModels/Authorization/AuthorizeViewModel.cs new file mode 100644 index 00000000..1ba43d4d --- /dev/null +++ b/samples/Mvc.Server/ViewModels/Authorization/AuthorizeViewModel.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace Mvc.Server.ViewModels.Authorization { + public class AuthorizeViewModel { + [Display(Name = "Application")] + public string ApplicationName { get; set; } + + [BindNever] + public IDictionary Parameters { get; set; } + + [Display(Name = "Scope")] + public string Scope { get; set; } + } +} diff --git a/samples/Mvc.Server/ViewModels/Authorization/ErrorViewModel.cs b/samples/Mvc.Server/ViewModels/Authorization/ErrorViewModel.cs new file mode 100644 index 00000000..631083d4 --- /dev/null +++ b/samples/Mvc.Server/ViewModels/Authorization/ErrorViewModel.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; + +namespace Mvc.Server.ViewModels.Authorization { + public class ErrorViewModel { + [Display(Name = "Error")] + public string Error { get; set; } + + [Display(Name = "Description")] + public string ErrorDescription { get; set; } + } +} diff --git a/samples/Mvc.Server/ViewModels/Authorization/LogoutViewModel.cs b/samples/Mvc.Server/ViewModels/Authorization/LogoutViewModel.cs new file mode 100644 index 00000000..756ad893 --- /dev/null +++ b/samples/Mvc.Server/ViewModels/Authorization/LogoutViewModel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace Mvc.Server.ViewModels.Authorization { + public class LogoutViewModel { + [BindNever] + public IDictionary Parameters { get; set; } + } +} diff --git a/samples/Mvc.Server/Views/Authorization/Authorize.cshtml b/samples/Mvc.Server/Views/Authorization/Authorize.cshtml new file mode 100644 index 00000000..8e544ae6 --- /dev/null +++ b/samples/Mvc.Server/Views/Authorization/Authorize.cshtml @@ -0,0 +1,18 @@ +@model AuthorizeViewModel + +
+

Authorization

+ +

Do you want to grant @Model.ApplicationName access to your data? (scopes requested: @Model.Scope)

+ +
+ @Html.AntiForgeryToken() + + @foreach (var parameter in Model.Parameters) { + + } + + + +
+
diff --git a/samples/Mvc.Server/Views/OpenIddict/Error.cshtml b/samples/Mvc.Server/Views/Authorization/Error.cshtml similarity index 77% rename from samples/Mvc.Server/Views/OpenIddict/Error.cshtml rename to samples/Mvc.Server/Views/Authorization/Error.cshtml index 4c210904..efe5e0a2 100644 --- a/samples/Mvc.Server/Views/OpenIddict/Error.cshtml +++ b/samples/Mvc.Server/Views/Authorization/Error.cshtml @@ -1,10 +1,10 @@ -@using Microsoft.IdentityModel.Protocols.OpenIdConnect -@model OpenIdConnectMessage +@model ErrorViewModel

Ooooops, something went really bad with your OpenID Connect request! :(

@Model.Error + @if (!string.IsNullOrEmpty(Model.ErrorDescription)) { @Model.ErrorDescription } diff --git a/src/OpenIddict.Mvc/Views/Shared/Logout.cshtml b/samples/Mvc.Server/Views/Authorization/Logout.cshtml similarity index 52% rename from src/OpenIddict.Mvc/Views/Shared/Logout.cshtml rename to samples/Mvc.Server/Views/Authorization/Logout.cshtml index 343082bb..9f54d61d 100644 --- a/src/OpenIddict.Mvc/Views/Shared/Logout.cshtml +++ b/samples/Mvc.Server/Views/Authorization/Logout.cshtml @@ -1,14 +1,8 @@ -@using Microsoft.IdentityModel.Protocols.OpenIdConnect -@model OpenIdConnectMessage - -

+

Log out

Are you sure you want to sign out?

-
- @Html.AntiForgeryToken() - - @* Flow the logout request *@ + @foreach (var parameter in Model.Parameters) { } diff --git a/samples/Mvc.Server/Views/_ViewImports.cshtml b/samples/Mvc.Server/Views/_ViewImports.cshtml index 148cc29f..c2721a8f 100644 --- a/samples/Mvc.Server/Views/_ViewImports.cshtml +++ b/samples/Mvc.Server/Views/_ViewImports.cshtml @@ -1,6 +1,7 @@ @using Mvc.Server @using Mvc.Server.Models @using Mvc.Server.ViewModels.Account +@using Mvc.Server.ViewModels.Authorization @using Mvc.Server.ViewModels.Manage @using Microsoft.AspNetCore.Identity @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/samples/Mvc.Server/project.json b/samples/Mvc.Server/project.json index 27bcf95b..630bc91a 100644 --- a/samples/Mvc.Server/project.json +++ b/samples/Mvc.Server/project.json @@ -35,10 +35,8 @@ "Microsoft.Extensions.Configuration.Json": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0", - "OpenIddict": { "target": "project" }, - "OpenIddict.Assets": { "target": "project" }, - "OpenIddict.Mvc": { "target": "project" }, - "OpenIddict.Security": { "target": "project" } + "NWebsec.AspNetCore.Middleware": "1.0.0-gamma1-15", + "OpenIddict": { "target": "project" } }, "frameworks": { diff --git a/src/OpenIddict.Assets/OpenIddict.Assets.xproj b/src/OpenIddict.Assets/OpenIddict.Assets.xproj deleted file mode 100644 index bd411291..00000000 --- a/src/OpenIddict.Assets/OpenIddict.Assets.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 86293e11-dd31-4d54-bcad-8788b5c9972f - OpenIddict.Assets - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/src/OpenIddict.Assets/OpenIddictExtensions.cs b/src/OpenIddict.Assets/OpenIddictExtensions.cs deleted file mode 100644 index 6ef7bc10..00000000 --- a/src/OpenIddict.Assets/OpenIddictExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * See https://github.com/openiddict/openiddict-core for more information concerning - * the license and the contributors participating to this project. - */ - -using System; -using System.Reflection; -using JetBrains.Annotations; -using Microsoft.Extensions.FileProviders; - -namespace Microsoft.AspNetCore.Builder { - public static class OpenIddictExtensions { - /// - /// Registers the assets module, including the default HTML/CSS files used by the MVC module. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AddAssets([NotNull] this OpenIddictBuilder builder) { - if (builder == null) { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.AddModule("Assets", -20, app => app.UseStaticFiles(new StaticFileOptions { - FileProvider = new EmbeddedFileProvider( - assembly: Assembly.Load(new AssemblyName("OpenIddict.Assets")), - baseNamespace: "OpenIddict.Assets") - })); - } - } -} \ No newline at end of file diff --git a/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.eot b/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index 4a4ca865..00000000 Binary files a/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.svg b/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.svg deleted file mode 100644 index e3e2dc73..00000000 --- a/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.svg +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.ttf b/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 67fa00bf..00000000 Binary files a/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.woff b/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.woff deleted file mode 100644 index 8c54182a..00000000 Binary files a/src/OpenIddict.Assets/fonts/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/src/OpenIddict.Assets/project.json b/src/OpenIddict.Assets/project.json deleted file mode 100644 index 7b305091..00000000 --- a/src/OpenIddict.Assets/project.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "version": "1.0.0-alpha2-*", - - "description": "Contains the default assets used by OpenIddict.", - "authors": [ "Kévin Chalet" ], - - "packOptions": { - "owners": [ "Kévin Chalet" ], - - "projectUrl": "https://github.com/openiddict/openiddict-core", - "iconUrl": "https://avatars3.githubusercontent.com/u/13908567?s=64", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html", - - "repository": { - "type": "git", - "url": "git://github.com/openiddict/openiddict-core" - }, - - "tags": [ - "aspnetcore", - "authentication", - "jwt", - "openidconnect", - "openiddict", - "security" - ] - }, - - "buildOptions": { - "warningsAsErrors": true, - "nowarn": [ "CS1591" ], - "xmlDoc": true, - - "embed": { - "include": [ - "fonts/*", - "scripts/*", - "stylesheets/*" - ] - } - }, - - "dependencies": { - "JetBrains.Annotations": { "type": "build", "version": "10.1.4" }, - "Microsoft.AspNetCore.StaticFiles": "1.0.0", - "Microsoft.Extensions.FileProviders.Embedded": "1.0.0", - "OpenIddict.Core": { "target": "project" } - }, - - "frameworks": { - "net451": { }, - - "netstandard1.4": { - "imports": [ - "dotnet5.5", - "portable-net451+win8" - ] - } - } -} \ No newline at end of file diff --git a/src/OpenIddict.Assets/scripts/bootstrap.js b/src/OpenIddict.Assets/scripts/bootstrap.js deleted file mode 100644 index 53da1c77..00000000 --- a/src/OpenIddict.Assets/scripts/bootstrap.js +++ /dev/null @@ -1,2114 +0,0 @@ -/*! - * Bootstrap v3.2.0 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ - -if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } - -/* ======================================================================== - * Bootstrap: transition.js v3.2.0 - * http://getbootstrap.com/javascript/#transitions - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) - // ============================================================ - - function transitionEnd() { - var el = document.createElement('bootstrap') - - var transEndEventNames = { - WebkitTransition : 'webkitTransitionEnd', - MozTransition : 'transitionend', - OTransition : 'oTransitionEnd otransitionend', - transition : 'transitionend' - } - - for (var name in transEndEventNames) { - if (el.style[name] !== undefined) { - return { end: transEndEventNames[name] } - } - } - - return false // explicit for ie8 ( ._.) - } - - // http://blog.alexmaccaw.com/css-transitions - $.fn.emulateTransitionEnd = function (duration) { - var called = false - var $el = this - $(this).one('bsTransitionEnd', function () { called = true }) - var callback = function () { if (!called) $($el).trigger($.support.transition.end) } - setTimeout(callback, duration) - return this - } - - $(function () { - $.support.transition = transitionEnd() - - if (!$.support.transition) return - - $.event.special.bsTransitionEnd = { - bindType: $.support.transition.end, - delegateType: $.support.transition.end, - handle: function (e) { - if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) - } - } - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: alert.js v3.2.0 - * http://getbootstrap.com/javascript/#alerts - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // ALERT CLASS DEFINITION - // ====================== - - var dismiss = '[data-dismiss="alert"]' - var Alert = function (el) { - $(el).on('click', dismiss, this.close) - } - - Alert.VERSION = '3.2.0' - - Alert.prototype.close = function (e) { - var $this = $(this) - var selector = $this.attr('data-target') - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 - } - - var $parent = $(selector) - - if (e) e.preventDefault() - - if (!$parent.length) { - $parent = $this.hasClass('alert') ? $this : $this.parent() - } - - $parent.trigger(e = $.Event('close.bs.alert')) - - if (e.isDefaultPrevented()) return - - $parent.removeClass('in') - - function removeElement() { - // detach from parent, fire event then clean up data - $parent.detach().trigger('closed.bs.alert').remove() - } - - $.support.transition && $parent.hasClass('fade') ? - $parent - .one('bsTransitionEnd', removeElement) - .emulateTransitionEnd(150) : - removeElement() - } - - - // ALERT PLUGIN DEFINITION - // ======================= - - function Plugin(option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.alert') - - if (!data) $this.data('bs.alert', (data = new Alert(this))) - if (typeof option == 'string') data[option].call($this) - }) - } - - var old = $.fn.alert - - $.fn.alert = Plugin - $.fn.alert.Constructor = Alert - - - // ALERT NO CONFLICT - // ================= - - $.fn.alert.noConflict = function () { - $.fn.alert = old - return this - } - - - // ALERT DATA-API - // ============== - - $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: button.js v3.2.0 - * http://getbootstrap.com/javascript/#buttons - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // BUTTON PUBLIC CLASS DEFINITION - // ============================== - - var Button = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, Button.DEFAULTS, options) - this.isLoading = false - } - - Button.VERSION = '3.2.0' - - Button.DEFAULTS = { - loadingText: 'loading...' - } - - Button.prototype.setState = function (state) { - var d = 'disabled' - var $el = this.$element - var val = $el.is('input') ? 'val' : 'html' - var data = $el.data() - - state = state + 'Text' - - if (data.resetText == null) $el.data('resetText', $el[val]()) - - $el[val](data[state] == null ? this.options[state] : data[state]) - - // push to event loop to allow forms to submit - setTimeout($.proxy(function () { - if (state == 'loadingText') { - this.isLoading = true - $el.addClass(d).attr(d, d) - } else if (this.isLoading) { - this.isLoading = false - $el.removeClass(d).removeAttr(d) - } - }, this), 0) - } - - Button.prototype.toggle = function () { - var changed = true - var $parent = this.$element.closest('[data-toggle="buttons"]') - - if ($parent.length) { - var $input = this.$element.find('input') - if ($input.prop('type') == 'radio') { - if ($input.prop('checked') && this.$element.hasClass('active')) changed = false - else $parent.find('.active').removeClass('active') - } - if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') - } - - if (changed) this.$element.toggleClass('active') - } - - - // BUTTON PLUGIN DEFINITION - // ======================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.button') - var options = typeof option == 'object' && option - - if (!data) $this.data('bs.button', (data = new Button(this, options))) - - if (option == 'toggle') data.toggle() - else if (option) data.setState(option) - }) - } - - var old = $.fn.button - - $.fn.button = Plugin - $.fn.button.Constructor = Button - - - // BUTTON NO CONFLICT - // ================== - - $.fn.button.noConflict = function () { - $.fn.button = old - return this - } - - - // BUTTON DATA-API - // =============== - - $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') - Plugin.call($btn, 'toggle') - e.preventDefault() - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: carousel.js v3.2.0 - * http://getbootstrap.com/javascript/#carousel - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // CAROUSEL CLASS DEFINITION - // ========================= - - var Carousel = function (element, options) { - this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this)) - this.$indicators = this.$element.find('.carousel-indicators') - this.options = options - this.paused = - this.sliding = - this.interval = - this.$active = - this.$items = null - - this.options.pause == 'hover' && this.$element - .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) - .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) - } - - Carousel.VERSION = '3.2.0' - - Carousel.DEFAULTS = { - interval: 5000, - pause: 'hover', - wrap: true - } - - Carousel.prototype.keydown = function (e) { - switch (e.which) { - case 37: this.prev(); break - case 39: this.next(); break - default: return - } - - e.preventDefault() - } - - Carousel.prototype.cycle = function (e) { - e || (this.paused = false) - - this.interval && clearInterval(this.interval) - - this.options.interval - && !this.paused - && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) - - return this - } - - Carousel.prototype.getItemIndex = function (item) { - this.$items = item.parent().children('.item') - return this.$items.index(item || this.$active) - } - - Carousel.prototype.to = function (pos) { - var that = this - var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) - - if (pos > (this.$items.length - 1) || pos < 0) return - - if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" - if (activeIndex == pos) return this.pause().cycle() - - return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) - } - - Carousel.prototype.pause = function (e) { - e || (this.paused = true) - - if (this.$element.find('.next, .prev').length && $.support.transition) { - this.$element.trigger($.support.transition.end) - this.cycle(true) - } - - this.interval = clearInterval(this.interval) - - return this - } - - Carousel.prototype.next = function () { - if (this.sliding) return - return this.slide('next') - } - - Carousel.prototype.prev = function () { - if (this.sliding) return - return this.slide('prev') - } - - Carousel.prototype.slide = function (type, next) { - var $active = this.$element.find('.item.active') - var $next = next || $active[type]() - var isCycling = this.interval - var direction = type == 'next' ? 'left' : 'right' - var fallback = type == 'next' ? 'first' : 'last' - var that = this - - if (!$next.length) { - if (!this.options.wrap) return - $next = this.$element.find('.item')[fallback]() - } - - if ($next.hasClass('active')) return (this.sliding = false) - - var relatedTarget = $next[0] - var slideEvent = $.Event('slide.bs.carousel', { - relatedTarget: relatedTarget, - direction: direction - }) - this.$element.trigger(slideEvent) - if (slideEvent.isDefaultPrevented()) return - - this.sliding = true - - isCycling && this.pause() - - if (this.$indicators.length) { - this.$indicators.find('.active').removeClass('active') - var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) - $nextIndicator && $nextIndicator.addClass('active') - } - - var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" - if ($.support.transition && this.$element.hasClass('slide')) { - $next.addClass(type) - $next[0].offsetWidth // force reflow - $active.addClass(direction) - $next.addClass(direction) - $active - .one('bsTransitionEnd', function () { - $next.removeClass([type, direction].join(' ')).addClass('active') - $active.removeClass(['active', direction].join(' ')) - that.sliding = false - setTimeout(function () { - that.$element.trigger(slidEvent) - }, 0) - }) - .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) - } else { - $active.removeClass('active') - $next.addClass('active') - this.sliding = false - this.$element.trigger(slidEvent) - } - - isCycling && this.cycle() - - return this - } - - - // CAROUSEL PLUGIN DEFINITION - // ========================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.carousel') - var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) - var action = typeof option == 'string' ? option : options.slide - - if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) - if (typeof option == 'number') data.to(option) - else if (action) data[action]() - else if (options.interval) data.pause().cycle() - }) - } - - var old = $.fn.carousel - - $.fn.carousel = Plugin - $.fn.carousel.Constructor = Carousel - - - // CAROUSEL NO CONFLICT - // ==================== - - $.fn.carousel.noConflict = function () { - $.fn.carousel = old - return this - } - - - // CAROUSEL DATA-API - // ================= - - $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { - var href - var $this = $(this) - var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 - if (!$target.hasClass('carousel')) return - var options = $.extend({}, $target.data(), $this.data()) - var slideIndex = $this.attr('data-slide-to') - if (slideIndex) options.interval = false - - Plugin.call($target, options) - - if (slideIndex) { - $target.data('bs.carousel').to(slideIndex) - } - - e.preventDefault() - }) - - $(window).on('load', function () { - $('[data-ride="carousel"]').each(function () { - var $carousel = $(this) - Plugin.call($carousel, $carousel.data()) - }) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: collapse.js v3.2.0 - * http://getbootstrap.com/javascript/#collapse - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // COLLAPSE PUBLIC CLASS DEFINITION - // ================================ - - var Collapse = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, Collapse.DEFAULTS, options) - this.transitioning = null - - if (this.options.parent) this.$parent = $(this.options.parent) - if (this.options.toggle) this.toggle() - } - - Collapse.VERSION = '3.2.0' - - Collapse.DEFAULTS = { - toggle: true - } - - Collapse.prototype.dimension = function () { - var hasWidth = this.$element.hasClass('width') - return hasWidth ? 'width' : 'height' - } - - Collapse.prototype.show = function () { - if (this.transitioning || this.$element.hasClass('in')) return - - var startEvent = $.Event('show.bs.collapse') - this.$element.trigger(startEvent) - if (startEvent.isDefaultPrevented()) return - - var actives = this.$parent && this.$parent.find('> .panel > .in') - - if (actives && actives.length) { - var hasData = actives.data('bs.collapse') - if (hasData && hasData.transitioning) return - Plugin.call(actives, 'hide') - hasData || actives.data('bs.collapse', null) - } - - var dimension = this.dimension() - - this.$element - .removeClass('collapse') - .addClass('collapsing')[dimension](0) - - this.transitioning = 1 - - var complete = function () { - this.$element - .removeClass('collapsing') - .addClass('collapse in')[dimension]('') - this.transitioning = 0 - this.$element - .trigger('shown.bs.collapse') - } - - if (!$.support.transition) return complete.call(this) - - var scrollSize = $.camelCase(['scroll', dimension].join('-')) - - this.$element - .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]) - } - - Collapse.prototype.hide = function () { - if (this.transitioning || !this.$element.hasClass('in')) return - - var startEvent = $.Event('hide.bs.collapse') - this.$element.trigger(startEvent) - if (startEvent.isDefaultPrevented()) return - - var dimension = this.dimension() - - this.$element[dimension](this.$element[dimension]())[0].offsetHeight - - this.$element - .addClass('collapsing') - .removeClass('collapse') - .removeClass('in') - - this.transitioning = 1 - - var complete = function () { - this.transitioning = 0 - this.$element - .trigger('hidden.bs.collapse') - .removeClass('collapsing') - .addClass('collapse') - } - - if (!$.support.transition) return complete.call(this) - - this.$element - [dimension](0) - .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(350) - } - - Collapse.prototype.toggle = function () { - this[this.$element.hasClass('in') ? 'hide' : 'show']() - } - - - // COLLAPSE PLUGIN DEFINITION - // ========================== - - function Plugin(option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.collapse') - var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) - - if (!data && options.toggle && option == 'show') option = !option - if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.collapse - - $.fn.collapse = Plugin - $.fn.collapse.Constructor = Collapse - - - // COLLAPSE NO CONFLICT - // ==================== - - $.fn.collapse.noConflict = function () { - $.fn.collapse = old - return this - } - - - // COLLAPSE DATA-API - // ================= - - $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { - var href - var $this = $(this) - var target = $this.attr('data-target') - || e.preventDefault() - || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 - var $target = $(target) - var data = $target.data('bs.collapse') - var option = data ? 'toggle' : $this.data() - var parent = $this.attr('data-parent') - var $parent = parent && $(parent) - - if (!data || !data.transitioning) { - if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed') - $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') - } - - Plugin.call($target, option) - }) - -}(jQuery); - -/* ======================================================================== - * Bootstrap: dropdown.js v3.2.0 - * http://getbootstrap.com/javascript/#dropdowns - * ======================================================================== - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - * ======================================================================== */ - - -+function ($) { - 'use strict'; - - // DROPDOWN CLASS DEFINITION - // ========================= - - var backdrop = '.dropdown-backdrop' - var toggle = '[data-toggle="dropdown"]' - var Dropdown = function (element) { - $(element).on('click.bs.dropdown', this.toggle) - } - - Dropdown.VERSION = '3.2.0' - - Dropdown.prototype.toggle = function (e) { - var $this = $(this) - - if ($this.is('.disabled, :disabled')) return - - var $parent = getParent($this) - var isActive = $parent.hasClass('open') - - clearMenus() - - if (!isActive) { - if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { - // if mobile we use a backdrop because click events don't delegate - $('