160 changed files with 63891 additions and 247 deletions
@ -0,0 +1,8 @@ |
|||
<Project> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="MSBuild.Microsoft.VisualStudio.Web.targets" Version="14.0.0.3" IsImplicitlyDefined="true" /> |
|||
<PackageReference Include="NuGet.Build.Tasks.Pack" Version="5.10.0" IsImplicitlyDefined="true" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,30 @@ |
|||
using System.Web; |
|||
using System.Web.Optimization; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Client |
|||
{ |
|||
public class BundleConfig |
|||
{ |
|||
// Pour plus d'informations sur le regroupement, visitez https://go.microsoft.com/fwlink/?LinkId=301862
|
|||
public static void RegisterBundles(BundleCollection bundles) |
|||
{ |
|||
bundles.Add(new ScriptBundle("~/bundles/jquery").Include( |
|||
"~/Scripts/jquery-{version}.js")); |
|||
|
|||
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( |
|||
"~/Scripts/jquery.validate*")); |
|||
|
|||
// Utilisez la version de développement de Modernizr pour développer et apprendre. Puis, lorsque vous êtes
|
|||
// prêt pour la production, utilisez l'outil de génération à l'adresse https://modernizr.com pour sélectionner uniquement les tests dont vous avez besoin.
|
|||
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( |
|||
"~/Scripts/modernizr-*")); |
|||
|
|||
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( |
|||
"~/Scripts/bootstrap.js")); |
|||
|
|||
bundles.Add(new StyleBundle("~/Content/css").Include( |
|||
"~/Content/bootstrap.css", |
|||
"~/Content/site.css")); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Client |
|||
{ |
|||
public class FilterConfig |
|||
{ |
|||
public static void RegisterGlobalFilters(GlobalFilterCollection filters) |
|||
{ |
|||
filters.Add(new HandleErrorAttribute()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using System.Web.Routing; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Client |
|||
{ |
|||
public class RouteConfig |
|||
{ |
|||
public static void RegisterRoutes(RouteCollection routes) |
|||
{ |
|||
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); |
|||
|
|||
routes.MapMvcAttributeRoutes(); |
|||
|
|||
routes.MapRoute( |
|||
name: "Default", |
|||
url: "{controller}/{action}/{id}", |
|||
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using Microsoft.Owin.Security; |
|||
using Microsoft.Owin.Security.Cookies; |
|||
using OpenIddict.Client.Owin; |
|||
using static OpenIddict.Abstractions.OpenIddictConstants; |
|||
using static OpenIddict.Client.Owin.OpenIddictClientOwinConstants; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Client.Controllers |
|||
{ |
|||
public class AuthenticationController : Controller |
|||
{ |
|||
[HttpGet, Route("~/login")] |
|||
public ActionResult LogIn(string returnUrl) |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
|
|||
var properties = new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
// Note: when only one client is registered in the client options,
|
|||
// setting the issuer property is not required and can be omitted.
|
|||
[OpenIddictClientOwinConstants.Properties.Issuer] = "https://localhost:44349/" |
|||
}) |
|||
{ |
|||
// Only allow local return URLs to prevent open redirect attacks.
|
|||
RedirectUri = Url.IsLocalUrl(returnUrl) ? returnUrl : "/" |
|||
}; |
|||
|
|||
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
|
|||
context.Authentication.Challenge(properties, OpenIddictClientOwinDefaults.AuthenticationType); |
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
[AcceptVerbs("GET", "POST"), Route("~/signin-oidc")] |
|||
public async Task<ActionResult> Callback() |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
|
|||
// Retrieve the authorization data validated by OpenIddict as part of the callback handling.
|
|||
var result = await context.Authentication.AuthenticateAsync(OpenIddictClientOwinDefaults.AuthenticationType); |
|||
|
|||
// Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons:
|
|||
//
|
|||
// * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable
|
|||
// for applications that don't need a long-term access to the user's resources or don't want to store
|
|||
// access/refresh tokens in a database or in an authentication cookie (which has security implications).
|
|||
// It is also suitable for applications that don't need to authenticate users but only need to perform
|
|||
// action(s) on their behalf by making API calls using the access token returned by the remote server.
|
|||
//
|
|||
// * Storing the external claims/tokens in a database (and optionally keeping the essentials claims in an
|
|||
// authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET
|
|||
// Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens.
|
|||
//
|
|||
// Note: in this case, it's recommended to use column encryption to protect the tokens in the database.
|
|||
//
|
|||
// * Storing the external claims/tokens in an authentication cookie, which doesn't require having
|
|||
// a user database but may be affected by the cookie size limits enforced by most browser vendors
|
|||
// (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies).
|
|||
//
|
|||
// Note: this is the approach used here, but the external claims are first filtered to only persist
|
|||
// a few claims like the user identifier. The same approach is used to store the access/refresh tokens.
|
|||
|
|||
// Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint,
|
|||
// result.Principal.Identity will represent an unauthenticated identity and won't contain any claim.
|
|||
//
|
|||
// Such identities cannot be used as-is to build an authentication cookie in ASP.NET (as the
|
|||
// antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but
|
|||
// the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.
|
|||
if (result.Identity is not ClaimsIdentity { IsAuthenticated: true }) |
|||
{ |
|||
throw new InvalidOperationException("The external authorization data cannot be used for authentication."); |
|||
} |
|||
|
|||
// Build an identity based on the external claims and that will be used to create the authentication cookie.
|
|||
//
|
|||
// By default, all claims extracted during the authorization dance are available. The claims collection stored
|
|||
// in the cookie can be filtered out or mapped to different names depending the claim name or its issuer.
|
|||
var claims = new List<Claim>(result.Identity.Claims |
|||
.Select(claim => claim switch |
|||
{ |
|||
// Map the standard "sub" claim to http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier,
|
|||
// which is the default claim type used by ASP.NET and is required by the antiforgery components.
|
|||
{ Type: Claims.Subject } |
|||
=> new Claim(ClaimTypes.NameIdentifier, claim.Value, claim.ValueType, claim.Issuer), |
|||
|
|||
// Map the standard "name" claim to http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name.
|
|||
{ Type: Claims.Name } |
|||
=> new Claim(ClaimTypes.Name, claim.Value, claim.ValueType, claim.Issuer), |
|||
|
|||
// Applications can map non-standard claims issued by specific issuers to a standard equivalent.
|
|||
{ Type: "non_standard_user_id", Issuer: "https://example.com/" } |
|||
=> new Claim(Claims.Subject, claim.Value, claim.ValueType, claim.Issuer), |
|||
|
|||
_ => claim |
|||
}) |
|||
.Where(claim => claim switch |
|||
{ |
|||
// Preserve the nameidentifier and name claims.
|
|||
{ Type: ClaimTypes.NameIdentifier or ClaimTypes.Name } => true, |
|||
|
|||
// Applications that use multiple client registrations can filter claims based on the issuer.
|
|||
{ Type: "custom_claim", Issuer: "https://example.com/" } => true, |
|||
|
|||
// Don't preserve the other claims.
|
|||
_ => false |
|||
})); |
|||
|
|||
// The antiforgery components require both the nameidentifier and identityprovider claims
|
|||
// so the latter is manually added using the issuer identity resolved from the remote server.
|
|||
claims.Add(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", |
|||
result.Identity.FindFirst(Claims.Subject).Issuer)); |
|||
|
|||
var identity = new ClaimsIdentity(claims, |
|||
authenticationType: CookieAuthenticationDefaults.AuthenticationType, |
|||
nameType: ClaimTypes.Name, |
|||
roleType: ClaimTypes.Role); |
|||
|
|||
// If needed, the tokens returned by the authorization server can be stored in the authentication cookie.
|
|||
var properties = new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
[Tokens.BackchannelAccessToken] = GetProperty(result.Properties, Tokens.BackchannelAccessToken), |
|||
[Tokens.RefreshToken] = GetProperty(result.Properties, Tokens.RefreshToken) |
|||
}) |
|||
{ |
|||
RedirectUri = result.Properties.RedirectUri |
|||
}; |
|||
|
|||
context.Authentication.SignIn(properties, identity); |
|||
return Redirect(properties.RedirectUri); |
|||
|
|||
static string? GetProperty(AuthenticationProperties properties, string name) |
|||
=> properties.Dictionary.TryGetValue(name, out var value) ? value : null; |
|||
} |
|||
|
|||
[AcceptVerbs("GET", "POST"), Route("~/logout")] |
|||
public ActionResult LogOut() |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
|
|||
// Ask the cookies middleware to delete the local cookie created when the user agent
|
|||
// is redirected from the identity provider after a successful authorization flow.
|
|||
var properties = new AuthenticationProperties |
|||
{ |
|||
RedirectUri = "/" |
|||
}; |
|||
|
|||
context.Authentication.SignOut(properties, CookieAuthenticationDefaults.AuthenticationType); |
|||
return Redirect(properties.RedirectUri); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
using System.Net.Http; |
|||
using System.Net.Http.Headers; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using Microsoft.Owin.Security.Cookies; |
|||
using static OpenIddict.Client.Owin.OpenIddictClientOwinConstants; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Client.Controllers |
|||
{ |
|||
public class HomeController : Controller |
|||
{ |
|||
private readonly IHttpClientFactory _httpClientFactory; |
|||
|
|||
public HomeController(IHttpClientFactory httpClientFactory) |
|||
=> _httpClientFactory = httpClientFactory; |
|||
|
|||
[HttpGet, Route("~/")] |
|||
public ActionResult Index() => View(); |
|||
|
|||
[Authorize, HttpPost, Route("~/")] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> Index(CancellationToken cancellationToken) |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
|
|||
var result = await context.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType); |
|||
var token = result.Properties.Dictionary[Tokens.BackchannelAccessToken]; |
|||
|
|||
using var client = _httpClientFactory.CreateClient(); |
|||
|
|||
using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44349/api/message"); |
|||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); |
|||
|
|||
using var response = await client.SendAsync(request, cancellationToken); |
|||
response.EnsureSuccessStatusCode(); |
|||
|
|||
return View(model: await response.Content.ReadAsStringAsync()); |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||
<%@ Application Codebehind="Global.asax.cs" Inherits="OpenIddict.Sandbox.AspNet.Client.MvcApplication" Language="C#" %> |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using System.Web.Optimization; |
|||
using System.Web.Routing; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Client |
|||
{ |
|||
public class MvcApplication : System.Web.HttpApplication |
|||
{ |
|||
protected void Application_Start() |
|||
{ |
|||
AreaRegistration.RegisterAllAreas(); |
|||
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); |
|||
RouteConfig.RegisterRoutes(RouteTable.Routes); |
|||
BundleConfig.RegisterBundles(BundleTable.Bundles); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
<Project Sdk="MSBuild.SDK.SystemWeb/4.0.77"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net48</TargetFramework> |
|||
<AppConfig>Web.config</AppConfig> |
|||
<GeneratedBindingRedirectsAction>Overwrite</GeneratedBindingRedirectsAction> |
|||
<VSToolsPath>$(NuGetPackageRoot)msbuild.microsoft.visualstudio.web.targets\14.0.0.3\tools\VSToolsPath</VSToolsPath> |
|||
<MvcBuildViews>false</MvcBuildViews> |
|||
<ImplicitUsings>disable</ImplicitUsings> |
|||
<NoWarn>CA3147</NoWarn> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<Reference Include="System.Web.Extensions" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\OpenIddict.Client.Owin\OpenIddict.Client.Owin.csproj" /> |
|||
<ProjectReference Include="..\..\src\OpenIddict.Client.SystemNetHttp\OpenIddict.Client.SystemNetHttp.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Antlr" /> |
|||
<PackageReference Include="Autofac.Extensions.DependencyInjection" /> |
|||
<PackageReference Include="Autofac.Mvc5" /> |
|||
<PackageReference Include="Autofac.Owin" /> |
|||
<PackageReference Include="Microsoft.AspNet.Mvc" /> |
|||
<PackageReference Include="Microsoft.AspNet.Web.Optimization" /> |
|||
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" /> |
|||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" /> |
|||
<PackageReference Include="Microsoft.Owin.Host.SystemWeb" /> |
|||
<PackageReference Include="Microsoft.Owin.Security.Cookies" /> |
|||
<PackageReference Include="Microsoft.Web.Infrastructure" PrivateAssets="all" /> |
|||
<PackageReference Include="Newtonsoft.Json" /> |
|||
<PackageReference Include="WebGrease" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,19 @@ |
|||
{ |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:52844/", |
|||
"sslPort": 44378 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"IIS Express": { |
|||
"commandName": "IISExpress", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,82 @@ |
|||
using System; |
|||
using System.Web.Mvc; |
|||
using Autofac; |
|||
using Autofac.Extensions.DependencyInjection; |
|||
using Autofac.Integration.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Owin.Security.Cookies; |
|||
using OpenIddict.Client; |
|||
using OpenIddict.Client.Owin; |
|||
using Owin; |
|||
using static OpenIddict.Abstractions.OpenIddictConstants; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Client |
|||
{ |
|||
public partial class Startup |
|||
{ |
|||
public void Configuration(IAppBuilder app) |
|||
{ |
|||
var container = CreateContainer(); |
|||
|
|||
// Register the Autofac scope injector middleware.
|
|||
app.UseAutofacLifetimeScopeInjector(container); |
|||
|
|||
// Register the cookie middleware responsible of storing the user sessions.
|
|||
app.UseCookieAuthentication(new CookieAuthenticationOptions |
|||
{ |
|||
ExpireTimeSpan = TimeSpan.FromMinutes(50), |
|||
SlidingExpiration = false |
|||
}); |
|||
|
|||
// Register the OpenIddict client middleware.
|
|||
app.UseMiddlewareFromContainer<OpenIddictClientOwinMiddleware>(); |
|||
|
|||
// Configure ASP.NET MVC 5.2 to use Autofac when activating controller instances.
|
|||
DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); |
|||
} |
|||
|
|||
private static IContainer CreateContainer() |
|||
{ |
|||
var services = new ServiceCollection(); |
|||
|
|||
services.AddOpenIddict() |
|||
.AddClient(options => |
|||
{ |
|||
// Add a client registration matching the client application definition in the server project.
|
|||
options.AddRegistration(new OpenIddictClientRegistration |
|||
{ |
|||
Issuer = new Uri("https://localhost:44349/", UriKind.Absolute), |
|||
|
|||
ClientId = "mvc", |
|||
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654", |
|||
RedirectUri = new Uri("https://localhost:44378/signin-oidc", UriKind.Absolute), |
|||
Scopes = { Scopes.Email, Scopes.Profile, Scopes.OfflineAccess, "demo_api" } |
|||
}); |
|||
|
|||
// Enable the redirection endpoint needed to handle the callback stage.
|
|||
options.SetRedirectionEndpointUris("/signin-oidc"); |
|||
|
|||
// Register the OWIN host and configure the OWIN-specific options.
|
|||
options.UseOwin() |
|||
.EnableRedirectionEndpointPassthrough(); |
|||
|
|||
// Register the signing and encryption credentials used to protect
|
|||
// sensitive data like the state tokens produced by OpenIddict.
|
|||
options.AddDevelopmentEncryptionCertificate() |
|||
.AddDevelopmentSigningCertificate(); |
|||
|
|||
// Register the System.Net.Http integration.
|
|||
options.UseSystemNetHttp(); |
|||
}); |
|||
|
|||
// Create a new Autofac container and import the OpenIddict services.
|
|||
var builder = new ContainerBuilder(); |
|||
builder.Populate(services); |
|||
|
|||
// Register the MVC controllers.
|
|||
builder.RegisterControllers(typeof(Startup).Assembly); |
|||
|
|||
return builder.Build(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,442 @@ |
|||
/*! |
|||
* Bootstrap v3.2.0 (http://getbootstrap.com) |
|||
* Copyright 2011-2014 Twitter, Inc. |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) |
|||
*/ |
|||
|
|||
.btn-default, |
|||
.btn-primary, |
|||
.btn-success, |
|||
.btn-info, |
|||
.btn-warning, |
|||
.btn-danger { |
|||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); |
|||
} |
|||
.btn-default:active, |
|||
.btn-primary:active, |
|||
.btn-success:active, |
|||
.btn-info:active, |
|||
.btn-warning:active, |
|||
.btn-danger:active, |
|||
.btn-default.active, |
|||
.btn-primary.active, |
|||
.btn-success.active, |
|||
.btn-info.active, |
|||
.btn-warning.active, |
|||
.btn-danger.active { |
|||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); |
|||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); |
|||
} |
|||
.btn:active, |
|||
.btn.active { |
|||
background-image: none; |
|||
} |
|||
.btn-default { |
|||
text-shadow: 0 1px 0 #fff; |
|||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); |
|||
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); |
|||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #dbdbdb; |
|||
border-color: #ccc; |
|||
} |
|||
.btn-default:hover, |
|||
.btn-default:focus { |
|||
background-color: #e0e0e0; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-default:active, |
|||
.btn-default.active { |
|||
background-color: #e0e0e0; |
|||
border-color: #dbdbdb; |
|||
} |
|||
.btn-default:disabled, |
|||
.btn-default[disabled] { |
|||
background-color: #e0e0e0; |
|||
background-image: none; |
|||
} |
|||
.btn-primary { |
|||
background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); |
|||
background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2)); |
|||
background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #2b669a; |
|||
} |
|||
.btn-primary:hover, |
|||
.btn-primary:focus { |
|||
background-color: #2d6ca2; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-primary:active, |
|||
.btn-primary.active { |
|||
background-color: #2d6ca2; |
|||
border-color: #2b669a; |
|||
} |
|||
.btn-primary:disabled, |
|||
.btn-primary[disabled] { |
|||
background-color: #2d6ca2; |
|||
background-image: none; |
|||
} |
|||
.btn-success { |
|||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); |
|||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); |
|||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #3e8f3e; |
|||
} |
|||
.btn-success:hover, |
|||
.btn-success:focus { |
|||
background-color: #419641; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-success:active, |
|||
.btn-success.active { |
|||
background-color: #419641; |
|||
border-color: #3e8f3e; |
|||
} |
|||
.btn-success:disabled, |
|||
.btn-success[disabled] { |
|||
background-color: #419641; |
|||
background-image: none; |
|||
} |
|||
.btn-info { |
|||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); |
|||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); |
|||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #28a4c9; |
|||
} |
|||
.btn-info:hover, |
|||
.btn-info:focus { |
|||
background-color: #2aabd2; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-info:active, |
|||
.btn-info.active { |
|||
background-color: #2aabd2; |
|||
border-color: #28a4c9; |
|||
} |
|||
.btn-info:disabled, |
|||
.btn-info[disabled] { |
|||
background-color: #2aabd2; |
|||
background-image: none; |
|||
} |
|||
.btn-warning { |
|||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); |
|||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); |
|||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #e38d13; |
|||
} |
|||
.btn-warning:hover, |
|||
.btn-warning:focus { |
|||
background-color: #eb9316; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-warning:active, |
|||
.btn-warning.active { |
|||
background-color: #eb9316; |
|||
border-color: #e38d13; |
|||
} |
|||
.btn-warning:disabled, |
|||
.btn-warning[disabled] { |
|||
background-color: #eb9316; |
|||
background-image: none; |
|||
} |
|||
.btn-danger { |
|||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); |
|||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); |
|||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #b92c28; |
|||
} |
|||
.btn-danger:hover, |
|||
.btn-danger:focus { |
|||
background-color: #c12e2a; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-danger:active, |
|||
.btn-danger.active { |
|||
background-color: #c12e2a; |
|||
border-color: #b92c28; |
|||
} |
|||
.btn-danger:disabled, |
|||
.btn-danger[disabled] { |
|||
background-color: #c12e2a; |
|||
background-image: none; |
|||
} |
|||
.thumbnail, |
|||
.img-thumbnail { |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075); |
|||
} |
|||
.dropdown-menu > li > a:hover, |
|||
.dropdown-menu > li > a:focus { |
|||
background-color: #e8e8e8; |
|||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); |
|||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.dropdown-menu > .active > a, |
|||
.dropdown-menu > .active > a:hover, |
|||
.dropdown-menu > .active > a:focus { |
|||
background-color: #357ebd; |
|||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); |
|||
background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); |
|||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.navbar-default { |
|||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); |
|||
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); |
|||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-radius: 4px; |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); |
|||
} |
|||
.navbar-default .navbar-nav > .active > a { |
|||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); |
|||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3)); |
|||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); |
|||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); |
|||
} |
|||
.navbar-brand, |
|||
.navbar-nav > li > a { |
|||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25); |
|||
} |
|||
.navbar-inverse { |
|||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); |
|||
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); |
|||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.navbar-inverse .navbar-nav > .active > a { |
|||
background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); |
|||
background-image: -o-linear-gradient(top, #222 0%, #282828 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828)); |
|||
background-image: linear-gradient(to bottom, #222 0%, #282828 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); |
|||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); |
|||
} |
|||
.navbar-inverse .navbar-brand, |
|||
.navbar-inverse .navbar-nav > li > a { |
|||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); |
|||
} |
|||
.navbar-static-top, |
|||
.navbar-fixed-top, |
|||
.navbar-fixed-bottom { |
|||
border-radius: 0; |
|||
} |
|||
.alert { |
|||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2); |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); |
|||
} |
|||
.alert-success { |
|||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); |
|||
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); |
|||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #b2dba1; |
|||
} |
|||
.alert-info { |
|||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); |
|||
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); |
|||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #9acfea; |
|||
} |
|||
.alert-warning { |
|||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); |
|||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); |
|||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #f5e79e; |
|||
} |
|||
.alert-danger { |
|||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); |
|||
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); |
|||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #dca7a7; |
|||
} |
|||
.progress { |
|||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); |
|||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); |
|||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar { |
|||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); |
|||
background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9)); |
|||
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-success { |
|||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); |
|||
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); |
|||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-info { |
|||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); |
|||
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); |
|||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-warning { |
|||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); |
|||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); |
|||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-danger { |
|||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); |
|||
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); |
|||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-striped { |
|||
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); |
|||
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); |
|||
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); |
|||
} |
|||
.list-group { |
|||
border-radius: 4px; |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075); |
|||
} |
|||
.list-group-item.active, |
|||
.list-group-item.active:hover, |
|||
.list-group-item.active:focus { |
|||
text-shadow: 0 -1px 0 #3071a9; |
|||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); |
|||
background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3)); |
|||
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #3278b3; |
|||
} |
|||
.panel { |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05); |
|||
} |
|||
.panel-default > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); |
|||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-primary > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); |
|||
background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); |
|||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-success > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); |
|||
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); |
|||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-info > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); |
|||
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); |
|||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-warning > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); |
|||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); |
|||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-danger > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); |
|||
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); |
|||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.well { |
|||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); |
|||
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); |
|||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #dcdcdc; |
|||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); |
|||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); |
|||
} |
|||
/*# sourceMappingURL=bootstrap-theme.css.map */ |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,79 @@ |
|||
/* Space out content a bit */ |
|||
body { |
|||
padding-top: 20px; |
|||
padding-bottom: 20px; |
|||
} |
|||
|
|||
/* Everything but the jumbotron gets side spacing for mobile first views */ |
|||
.header, |
|||
.marketing, |
|||
.footer { |
|||
padding-right: 15px; |
|||
padding-left: 15px; |
|||
} |
|||
|
|||
/* Custom page header */ |
|||
.header { |
|||
border-bottom: 1px solid #e5e5e5; |
|||
} |
|||
/* Make the masthead heading the same height as the navigation */ |
|||
.header h3 { |
|||
padding-bottom: 19px; |
|||
margin-top: 0; |
|||
margin-bottom: 0; |
|||
line-height: 40px; |
|||
} |
|||
|
|||
/* Custom page footer */ |
|||
.footer { |
|||
padding-top: 19px; |
|||
color: #777; |
|||
border-top: 1px solid #e5e5e5; |
|||
} |
|||
|
|||
/* Customize container */ |
|||
@media (min-width: 768px) { |
|||
.container { |
|||
max-width: 730px; |
|||
} |
|||
} |
|||
.container-narrow > hr { |
|||
margin: 30px 0; |
|||
} |
|||
|
|||
/* Main marketing message and sign up button */ |
|||
.jumbotron { |
|||
text-align: center; |
|||
border-bottom: 1px solid #e5e5e5; |
|||
} |
|||
.jumbotron .btn { |
|||
padding: 14px 24px; |
|||
font-size: 21px; |
|||
} |
|||
|
|||
/* Supporting marketing content */ |
|||
.marketing { |
|||
margin: 40px 0; |
|||
} |
|||
.marketing p + h4 { |
|||
margin-top: 28px; |
|||
} |
|||
|
|||
/* Responsive: Portrait tablets and up */ |
|||
@media screen and (min-width: 768px) { |
|||
/* Remove the padding we set earlier */ |
|||
.header, |
|||
.marketing, |
|||
.footer { |
|||
padding-right: 0; |
|||
padding-left: 0; |
|||
} |
|||
/* Space out the masthead */ |
|||
.header { |
|||
margin-bottom: 30px; |
|||
} |
|||
/* Remove the bottom border on the jumbotron for visual effect */ |
|||
.jumbotron { |
|||
border-bottom: 0; |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
@using System.Security.Claims |
|||
@model string |
|||
|
|||
<div class="jumbotron"> |
|||
@if (User?.Identity is { IsAuthenticated: true }) |
|||
{ |
|||
<h1>Welcome, @User.Identity.Name</h1> |
|||
|
|||
<p> |
|||
@foreach (var claim in ((ClaimsPrincipal) Context.User).Claims) |
|||
{ |
|||
<div>@claim.Type: <b>@claim.Value</b></div> |
|||
} |
|||
</p> |
|||
|
|||
if (!string.IsNullOrEmpty(Model)) |
|||
{ |
|||
<h3>Message received from the resource controller: @Model</h3> |
|||
} |
|||
|
|||
<form action="~/" method="post"> |
|||
@Html.AntiForgeryToken() |
|||
|
|||
<button class="btn btn-lg btn-warning" type="submit">Query the resource controller</button> |
|||
</form> |
|||
|
|||
<a class="btn btn-lg btn-danger" href="/logout">Sign out</a> |
|||
} |
|||
|
|||
else |
|||
{ |
|||
<h1>Welcome, anonymous</h1> |
|||
<a class="btn btn-lg btn-success" href="/login">Sign in</a> |
|||
} |
|||
</div> |
|||
@ -0,0 +1,9 @@ |
|||
@model System.Web.Mvc.HandleErrorInfo |
|||
|
|||
@{ |
|||
ViewBag.Title = "Erreur"; |
|||
} |
|||
|
|||
<h1 class="text-danger">Erreur.</h1> |
|||
<h2 class="text-danger">Une erreur s'est produite lors du traitement de votre demande.</h2> |
|||
|
|||
@ -0,0 +1,30 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
|||
<meta name="description" content="" /> |
|||
<meta name="author" content="" /> |
|||
|
|||
<title>OpenIddict.Sandbox.AspNet.Client</title> |
|||
|
|||
<link href="~/stylesheets/bootstrap.min.css" rel="stylesheet" /> |
|||
<link href="~/stylesheets/jumbotron-narrow.css" rel="stylesheet" /> |
|||
</head> |
|||
|
|||
<body> |
|||
<div class="container"> |
|||
<div class="header"> |
|||
<h3 class="text-muted">Your application (OpenIddict.Sandbox.AspNet.Client)</h3> |
|||
</div> |
|||
|
|||
@RenderBody() |
|||
|
|||
<div class="footer"> |
|||
<p>© Your company 2014</p> |
|||
</div> |
|||
|
|||
</div> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,43 @@ |
|||
<?xml version="1.0"?> |
|||
|
|||
<configuration> |
|||
<configSections> |
|||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> |
|||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" /> |
|||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" /> |
|||
</sectionGroup> |
|||
</configSections> |
|||
|
|||
<system.web.webPages.razor> |
|||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> |
|||
<pages pageBaseType="System.Web.Mvc.WebViewPage"> |
|||
<namespaces> |
|||
<add namespace="System.Web.Mvc" /> |
|||
<add namespace="System.Web.Mvc.Ajax" /> |
|||
<add namespace="System.Web.Mvc.Html" /> |
|||
<add namespace="System.Web.Optimization"/> |
|||
<add namespace="System.Web.Routing" /> |
|||
<add namespace="OpenIddict.Sandbox.AspNet.Client" /> |
|||
</namespaces> |
|||
</pages> |
|||
</system.web.webPages.razor> |
|||
|
|||
<appSettings> |
|||
<add key="webpages:Enabled" value="false" /> |
|||
</appSettings> |
|||
|
|||
<system.webServer> |
|||
<handlers> |
|||
<remove name="BlockViewHandler"/> |
|||
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" /> |
|||
</handlers> |
|||
</system.webServer> |
|||
|
|||
<system.web> |
|||
<compilation> |
|||
<assemblies> |
|||
<add assembly="System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> |
|||
</assemblies> |
|||
</compilation> |
|||
</system.web> |
|||
</configuration> |
|||
@ -0,0 +1,3 @@ |
|||
@{ |
|||
Layout = "~/Views/Shared/_Layout.cshtml"; |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
|
|||
<!-- Pour plus d'informations sur l'utilisation de la transformation Web.config, visitez https://go.microsoft.com/fwlink/?LinkId=301874 --> |
|||
|
|||
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> |
|||
<!-- |
|||
Dans l'exemple ci-dessous, la transformation "SetAttributes" changera la valeur de |
|||
"connectionString" afin d'utiliser "ReleaseSQLServer" uniquement lorsque le localisateur "Match" |
|||
trouve un attribut "name" qui a une valeur "MyDB" . |
|||
|
|||
<connectionStrings> |
|||
<add name="MyDB" |
|||
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" |
|||
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/> |
|||
</connectionStrings> |
|||
--> |
|||
<system.web> |
|||
<!-- |
|||
Dans l'exemple ci-dessous, la transformation « Replace » remplacera toute la |
|||
<customErrors> section de votre fichier Web.config. |
|||
Dans la mesure où il n'y a qu'une section customErrors sous le |
|||
<system.web> nœud, il n'est pas nécessaire d'utiliser l'attribut « xdt:Locator ». |
|||
|
|||
<customErrors defaultRedirect="GenericError.htm" |
|||
mode="RemoteOnly" xdt:Transform="Replace"> |
|||
<error statusCode="500" redirect="InternalError.htm"/> |
|||
</customErrors> |
|||
--> |
|||
</system.web> |
|||
</configuration> |
|||
@ -0,0 +1,31 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
|
|||
<!-- Pour plus d'informations sur l'utilisation de la transformation Web.config, visitez https://go.microsoft.com/fwlink/?LinkId=301874 --> |
|||
|
|||
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> |
|||
<!-- |
|||
Dans l'exemple ci-dessous, la transformation "SetAttributes" changera la valeur de |
|||
"connectionString" afin d'utiliser "ReleaseSQLServer" uniquement lorsque le localisateur "Match" |
|||
trouve un attribut "name" qui a une valeur "MyDB" . |
|||
|
|||
<connectionStrings> |
|||
<add name="MyDB" |
|||
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" |
|||
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/> |
|||
</connectionStrings> |
|||
--> |
|||
<system.web> |
|||
<compilation xdt:Transform="RemoveAttributes(debug)" /> |
|||
<!-- |
|||
Dans l'exemple ci-dessous, la transformation « Replace » remplacera toute la |
|||
<customErrors> section de votre fichier Web.config. |
|||
Dans la mesure où il n'y a qu'une section customErrors sous le |
|||
<system.web> nœud, il n'est pas nécessaire d'utiliser l'attribut « xdt:Locator ». |
|||
|
|||
<customErrors defaultRedirect="GenericError.htm" |
|||
mode="RemoteOnly" xdt:Transform="Replace"> |
|||
<error statusCode="500" redirect="InternalError.htm"/> |
|||
</customErrors> |
|||
--> |
|||
</system.web> |
|||
</configuration> |
|||
@ -0,0 +1,116 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
For more information on how to configure your ASP.NET application, please visit |
|||
https://go.microsoft.com/fwlink/?LinkId=301880 |
|||
--> |
|||
<configuration> |
|||
<appSettings> |
|||
<add key="webpages:Version" value="3.0.0.0" /> |
|||
<add key="webpages:Enabled" value="false" /> |
|||
<add key="ClientValidationEnabled" value="true" /> |
|||
<add key="UnobtrusiveJavaScriptEnabled" value="true" /> |
|||
</appSettings> |
|||
<system.web> |
|||
<compilation debug="true" targetFramework="4.8" /> |
|||
<httpRuntime targetFramework="4.8" maxQueryStringLength="32768" /> |
|||
</system.web> |
|||
<system.webServer> |
|||
<security> |
|||
<requestFiltering> |
|||
<requestLimits maxQueryString="32768" /> |
|||
</requestFiltering> |
|||
</security> |
|||
</system.webServer> |
|||
<runtime> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="Microsoft.Extensions.Caching.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-2.1.23.0" newVersion="2.1.23.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-2.1.1.0" newVersion="2.1.1.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="Microsoft.Extensions.Primitives" publicKeyToken="adb9793829ddae60" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-2.1.6.0" newVersion="2.1.6.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.1" newVersion="4.0.5.1" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> |
|||
<dependentAssembly> |
|||
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" /> |
|||
<bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" /> |
|||
</dependentAssembly> |
|||
</assemblyBinding> |
|||
</runtime> |
|||
<system.codedom> |
|||
<compilers> |
|||
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=3.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" /> |
|||
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=3.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" /> |
|||
</compilers> |
|||
</system.codedom> |
|||
</configuration> |
|||
|
After Width: | Height: | Size: 31 KiB |
@ -0,0 +1,30 @@ |
|||
using System.Web; |
|||
using System.Web.Optimization; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server |
|||
{ |
|||
public class BundleConfig |
|||
{ |
|||
// Pour plus d'informations sur le regroupement, visitez https://go.microsoft.com/fwlink/?LinkId=301862
|
|||
public static void RegisterBundles(BundleCollection bundles) |
|||
{ |
|||
bundles.Add(new ScriptBundle("~/bundles/jquery").Include( |
|||
"~/Scripts/jquery-{version}.js")); |
|||
|
|||
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( |
|||
"~/Scripts/jquery.validate*")); |
|||
|
|||
// Utilisez la version de développement de Modernizr pour développer et apprendre. Puis, lorsque vous êtes
|
|||
// prêt pour la production, utilisez l'outil de génération à l'adresse https://modernizr.com pour sélectionner uniquement les tests dont vous avez besoin.
|
|||
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( |
|||
"~/Scripts/modernizr-*")); |
|||
|
|||
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( |
|||
"~/Scripts/bootstrap.js")); |
|||
|
|||
bundles.Add(new StyleBundle("~/Content/css").Include( |
|||
"~/Content/bootstrap.css", |
|||
"~/Content/site.css")); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server |
|||
{ |
|||
public class FilterConfig |
|||
{ |
|||
public static void RegisterGlobalFilters(GlobalFilterCollection filters) |
|||
{ |
|||
filters.Add(new HandleErrorAttribute()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,109 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Data.Entity; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
using Microsoft.AspNet.Identity; |
|||
using Microsoft.AspNet.Identity.EntityFramework; |
|||
using Microsoft.AspNet.Identity.Owin; |
|||
using Microsoft.Owin; |
|||
using Microsoft.Owin.Security; |
|||
using OpenIddict.Sandbox.AspNet.Server.Models; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server |
|||
{ |
|||
public class EmailService : IIdentityMessageService |
|||
{ |
|||
public Task SendAsync(IdentityMessage message) |
|||
{ |
|||
// Connectez votre service e-mail ici pour envoyer un e-mail.
|
|||
return Task.FromResult(0); |
|||
} |
|||
} |
|||
|
|||
public class SmsService : IIdentityMessageService |
|||
{ |
|||
public Task SendAsync(IdentityMessage message) |
|||
{ |
|||
// Connectez votre service SMS ici pour envoyer un message texte.
|
|||
return Task.FromResult(0); |
|||
} |
|||
} |
|||
|
|||
// Configurer l'application que le gestionnaire des utilisateurs a utilisée dans cette application. UserManager est défini dans ASP.NET Identity et est utilisé par l'application.
|
|||
public class ApplicationUserManager : UserManager<ApplicationUser> |
|||
{ |
|||
public ApplicationUserManager(IUserStore<ApplicationUser> store) |
|||
: base(store) |
|||
{ |
|||
} |
|||
|
|||
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) |
|||
{ |
|||
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>())); |
|||
// Configurer la logique de validation pour les noms d'utilisateur
|
|||
manager.UserValidator = new UserValidator<ApplicationUser>(manager) |
|||
{ |
|||
AllowOnlyAlphanumericUserNames = false, |
|||
RequireUniqueEmail = true |
|||
}; |
|||
|
|||
// Configurer la logique de validation pour les mots de passe
|
|||
manager.PasswordValidator = new PasswordValidator |
|||
{ |
|||
RequiredLength = 6, |
|||
RequireNonLetterOrDigit = true, |
|||
RequireDigit = true, |
|||
RequireLowercase = true, |
|||
RequireUppercase = true, |
|||
}; |
|||
|
|||
// Configurer les valeurs par défaut du verrouillage de l'utilisateur
|
|||
manager.UserLockoutEnabledByDefault = true; |
|||
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5); |
|||
manager.MaxFailedAccessAttemptsBeforeLockout = 5; |
|||
|
|||
// Inscrire les fournisseurs d'authentification à 2 facteurs. Cette application utilise le téléphone et l'e-mail comme procédure de réception d'un code de vérification de l'utilisateur
|
|||
// Vous pouvez écrire votre propre fournisseur et le connecter ici.
|
|||
manager.RegisterTwoFactorProvider("Code téléphonique ", new PhoneNumberTokenProvider<ApplicationUser> |
|||
{ |
|||
MessageFormat = "Votre code de sécurité est {0}" |
|||
}); |
|||
manager.RegisterTwoFactorProvider("Code d'e-mail", new EmailTokenProvider<ApplicationUser> |
|||
{ |
|||
Subject = "Code de sécurité", |
|||
BodyFormat = "Votre code de sécurité est {0}" |
|||
}); |
|||
manager.EmailService = new EmailService(); |
|||
manager.SmsService = new SmsService(); |
|||
var dataProtectionProvider = options.DataProtectionProvider; |
|||
if (dataProtectionProvider != null) |
|||
{ |
|||
manager.UserTokenProvider = |
|||
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity")); |
|||
} |
|||
return manager; |
|||
} |
|||
} |
|||
|
|||
// Configurer le gestionnaire de connexion d'application qui est utilisé dans cette application.
|
|||
public class ApplicationSignInManager : SignInManager<ApplicationUser, string> |
|||
{ |
|||
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) |
|||
: base(userManager, authenticationManager) |
|||
{ |
|||
} |
|||
|
|||
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user) |
|||
{ |
|||
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager); |
|||
} |
|||
|
|||
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context) |
|||
{ |
|||
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System.Web.Mvc; |
|||
using System.Web.Routing; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server |
|||
{ |
|||
public class RouteConfig |
|||
{ |
|||
public static void RegisterRoutes(RouteCollection routes) |
|||
{ |
|||
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); |
|||
|
|||
routes.MapMvcAttributeRoutes(); |
|||
|
|||
routes.MapRoute( |
|||
name: "Default", |
|||
url: "{controller}/{action}/{id}", |
|||
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
using System; |
|||
using Microsoft.AspNet.Identity; |
|||
using Microsoft.AspNet.Identity.Owin; |
|||
using Microsoft.Owin; |
|||
using Microsoft.Owin.Security.Cookies; |
|||
using OpenIddict.Sandbox.AspNet.Server.Models; |
|||
using Owin; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server |
|||
{ |
|||
public partial class Startup |
|||
{ |
|||
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301883
|
|||
public void ConfigureAuth(IAppBuilder app) |
|||
{ |
|||
// Configure the db context, user manager and signin manager to use a single instance per request
|
|||
app.CreatePerOwinContext(ApplicationDbContext.Create); |
|||
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); |
|||
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); |
|||
|
|||
// Enable the application to use a cookie to store information for the signed in user
|
|||
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
|
|||
// Configure the sign in cookie
|
|||
app.UseCookieAuthentication(new CookieAuthenticationOptions |
|||
{ |
|||
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, |
|||
LoginPath = new PathString("/Account/Login"), |
|||
Provider = new CookieAuthenticationProvider |
|||
{ |
|||
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( |
|||
validateInterval: TimeSpan.FromMinutes(30), |
|||
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) |
|||
} |
|||
}); |
|||
// Use a cookie to temporarily store information about a user logging in with a third party login provider
|
|||
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); |
|||
|
|||
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
|
|||
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5)); |
|||
|
|||
// Enables the application to remember the second login verification factor such as phone or email.
|
|||
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
|
|||
// This is similar to the RememberMe option when you log in.
|
|||
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); |
|||
|
|||
// Uncomment the following lines to enable logging in with third party login providers
|
|||
//app.UseMicrosoftAccountAuthentication(
|
|||
// clientId: "",
|
|||
// clientSecret: "");
|
|||
|
|||
//app.UseTwitterAuthentication(
|
|||
// consumerKey: "",
|
|||
// consumerSecret: "");
|
|||
|
|||
//app.UseFacebookAuthentication(
|
|||
// appId: "",
|
|||
// appSecret: "");
|
|||
|
|||
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
|
|||
//{
|
|||
// ClientId = "",
|
|||
// ClientSecret = ""
|
|||
//});
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
body { |
|||
padding-top: 50px; |
|||
padding-bottom: 20px; |
|||
} |
|||
|
|||
/* Set padding to keep content from hitting the edges */ |
|||
.body-content { |
|||
padding-left: 15px; |
|||
padding-right: 15px; |
|||
} |
|||
|
|||
/* Override the default bootstrap behavior where horizontal description lists |
|||
will truncate terms that are too long to fit in the left column |
|||
*/ |
|||
.dl-horizontal dt { |
|||
white-space: normal; |
|||
} |
|||
|
|||
/* Set width on the form input elements since they're 100% wide by default */ |
|||
input, |
|||
select, |
|||
textarea { |
|||
max-width: 280px; |
|||
} |
|||
@ -0,0 +1,587 @@ |
|||
/*! |
|||
* Bootstrap v3.4.1 (https://getbootstrap.com/) |
|||
* Copyright 2011-2019 Twitter, Inc. |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) |
|||
*/ |
|||
.btn-default, |
|||
.btn-primary, |
|||
.btn-success, |
|||
.btn-info, |
|||
.btn-warning, |
|||
.btn-danger { |
|||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); |
|||
} |
|||
.btn-default:active, |
|||
.btn-primary:active, |
|||
.btn-success:active, |
|||
.btn-info:active, |
|||
.btn-warning:active, |
|||
.btn-danger:active, |
|||
.btn-default.active, |
|||
.btn-primary.active, |
|||
.btn-success.active, |
|||
.btn-info.active, |
|||
.btn-warning.active, |
|||
.btn-danger.active { |
|||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); |
|||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); |
|||
} |
|||
.btn-default.disabled, |
|||
.btn-primary.disabled, |
|||
.btn-success.disabled, |
|||
.btn-info.disabled, |
|||
.btn-warning.disabled, |
|||
.btn-danger.disabled, |
|||
.btn-default[disabled], |
|||
.btn-primary[disabled], |
|||
.btn-success[disabled], |
|||
.btn-info[disabled], |
|||
.btn-warning[disabled], |
|||
.btn-danger[disabled], |
|||
fieldset[disabled] .btn-default, |
|||
fieldset[disabled] .btn-primary, |
|||
fieldset[disabled] .btn-success, |
|||
fieldset[disabled] .btn-info, |
|||
fieldset[disabled] .btn-warning, |
|||
fieldset[disabled] .btn-danger { |
|||
-webkit-box-shadow: none; |
|||
box-shadow: none; |
|||
} |
|||
.btn-default .badge, |
|||
.btn-primary .badge, |
|||
.btn-success .badge, |
|||
.btn-info .badge, |
|||
.btn-warning .badge, |
|||
.btn-danger .badge { |
|||
text-shadow: none; |
|||
} |
|||
.btn:active, |
|||
.btn.active { |
|||
background-image: none; |
|||
} |
|||
.btn-default { |
|||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); |
|||
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); |
|||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #dbdbdb; |
|||
text-shadow: 0 1px 0 #fff; |
|||
border-color: #ccc; |
|||
} |
|||
.btn-default:hover, |
|||
.btn-default:focus { |
|||
background-color: #e0e0e0; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-default:active, |
|||
.btn-default.active { |
|||
background-color: #e0e0e0; |
|||
border-color: #dbdbdb; |
|||
} |
|||
.btn-default.disabled, |
|||
.btn-default[disabled], |
|||
fieldset[disabled] .btn-default, |
|||
.btn-default.disabled:hover, |
|||
.btn-default[disabled]:hover, |
|||
fieldset[disabled] .btn-default:hover, |
|||
.btn-default.disabled:focus, |
|||
.btn-default[disabled]:focus, |
|||
fieldset[disabled] .btn-default:focus, |
|||
.btn-default.disabled.focus, |
|||
.btn-default[disabled].focus, |
|||
fieldset[disabled] .btn-default.focus, |
|||
.btn-default.disabled:active, |
|||
.btn-default[disabled]:active, |
|||
fieldset[disabled] .btn-default:active, |
|||
.btn-default.disabled.active, |
|||
.btn-default[disabled].active, |
|||
fieldset[disabled] .btn-default.active { |
|||
background-color: #e0e0e0; |
|||
background-image: none; |
|||
} |
|||
.btn-primary { |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #245580; |
|||
} |
|||
.btn-primary:hover, |
|||
.btn-primary:focus { |
|||
background-color: #265a88; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-primary:active, |
|||
.btn-primary.active { |
|||
background-color: #265a88; |
|||
border-color: #245580; |
|||
} |
|||
.btn-primary.disabled, |
|||
.btn-primary[disabled], |
|||
fieldset[disabled] .btn-primary, |
|||
.btn-primary.disabled:hover, |
|||
.btn-primary[disabled]:hover, |
|||
fieldset[disabled] .btn-primary:hover, |
|||
.btn-primary.disabled:focus, |
|||
.btn-primary[disabled]:focus, |
|||
fieldset[disabled] .btn-primary:focus, |
|||
.btn-primary.disabled.focus, |
|||
.btn-primary[disabled].focus, |
|||
fieldset[disabled] .btn-primary.focus, |
|||
.btn-primary.disabled:active, |
|||
.btn-primary[disabled]:active, |
|||
fieldset[disabled] .btn-primary:active, |
|||
.btn-primary.disabled.active, |
|||
.btn-primary[disabled].active, |
|||
fieldset[disabled] .btn-primary.active { |
|||
background-color: #265a88; |
|||
background-image: none; |
|||
} |
|||
.btn-success { |
|||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); |
|||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); |
|||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #3e8f3e; |
|||
} |
|||
.btn-success:hover, |
|||
.btn-success:focus { |
|||
background-color: #419641; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-success:active, |
|||
.btn-success.active { |
|||
background-color: #419641; |
|||
border-color: #3e8f3e; |
|||
} |
|||
.btn-success.disabled, |
|||
.btn-success[disabled], |
|||
fieldset[disabled] .btn-success, |
|||
.btn-success.disabled:hover, |
|||
.btn-success[disabled]:hover, |
|||
fieldset[disabled] .btn-success:hover, |
|||
.btn-success.disabled:focus, |
|||
.btn-success[disabled]:focus, |
|||
fieldset[disabled] .btn-success:focus, |
|||
.btn-success.disabled.focus, |
|||
.btn-success[disabled].focus, |
|||
fieldset[disabled] .btn-success.focus, |
|||
.btn-success.disabled:active, |
|||
.btn-success[disabled]:active, |
|||
fieldset[disabled] .btn-success:active, |
|||
.btn-success.disabled.active, |
|||
.btn-success[disabled].active, |
|||
fieldset[disabled] .btn-success.active { |
|||
background-color: #419641; |
|||
background-image: none; |
|||
} |
|||
.btn-info { |
|||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); |
|||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); |
|||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #28a4c9; |
|||
} |
|||
.btn-info:hover, |
|||
.btn-info:focus { |
|||
background-color: #2aabd2; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-info:active, |
|||
.btn-info.active { |
|||
background-color: #2aabd2; |
|||
border-color: #28a4c9; |
|||
} |
|||
.btn-info.disabled, |
|||
.btn-info[disabled], |
|||
fieldset[disabled] .btn-info, |
|||
.btn-info.disabled:hover, |
|||
.btn-info[disabled]:hover, |
|||
fieldset[disabled] .btn-info:hover, |
|||
.btn-info.disabled:focus, |
|||
.btn-info[disabled]:focus, |
|||
fieldset[disabled] .btn-info:focus, |
|||
.btn-info.disabled.focus, |
|||
.btn-info[disabled].focus, |
|||
fieldset[disabled] .btn-info.focus, |
|||
.btn-info.disabled:active, |
|||
.btn-info[disabled]:active, |
|||
fieldset[disabled] .btn-info:active, |
|||
.btn-info.disabled.active, |
|||
.btn-info[disabled].active, |
|||
fieldset[disabled] .btn-info.active { |
|||
background-color: #2aabd2; |
|||
background-image: none; |
|||
} |
|||
.btn-warning { |
|||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); |
|||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); |
|||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #e38d13; |
|||
} |
|||
.btn-warning:hover, |
|||
.btn-warning:focus { |
|||
background-color: #eb9316; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-warning:active, |
|||
.btn-warning.active { |
|||
background-color: #eb9316; |
|||
border-color: #e38d13; |
|||
} |
|||
.btn-warning.disabled, |
|||
.btn-warning[disabled], |
|||
fieldset[disabled] .btn-warning, |
|||
.btn-warning.disabled:hover, |
|||
.btn-warning[disabled]:hover, |
|||
fieldset[disabled] .btn-warning:hover, |
|||
.btn-warning.disabled:focus, |
|||
.btn-warning[disabled]:focus, |
|||
fieldset[disabled] .btn-warning:focus, |
|||
.btn-warning.disabled.focus, |
|||
.btn-warning[disabled].focus, |
|||
fieldset[disabled] .btn-warning.focus, |
|||
.btn-warning.disabled:active, |
|||
.btn-warning[disabled]:active, |
|||
fieldset[disabled] .btn-warning:active, |
|||
.btn-warning.disabled.active, |
|||
.btn-warning[disabled].active, |
|||
fieldset[disabled] .btn-warning.active { |
|||
background-color: #eb9316; |
|||
background-image: none; |
|||
} |
|||
.btn-danger { |
|||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); |
|||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); |
|||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #b92c28; |
|||
} |
|||
.btn-danger:hover, |
|||
.btn-danger:focus { |
|||
background-color: #c12e2a; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-danger:active, |
|||
.btn-danger.active { |
|||
background-color: #c12e2a; |
|||
border-color: #b92c28; |
|||
} |
|||
.btn-danger.disabled, |
|||
.btn-danger[disabled], |
|||
fieldset[disabled] .btn-danger, |
|||
.btn-danger.disabled:hover, |
|||
.btn-danger[disabled]:hover, |
|||
fieldset[disabled] .btn-danger:hover, |
|||
.btn-danger.disabled:focus, |
|||
.btn-danger[disabled]:focus, |
|||
fieldset[disabled] .btn-danger:focus, |
|||
.btn-danger.disabled.focus, |
|||
.btn-danger[disabled].focus, |
|||
fieldset[disabled] .btn-danger.focus, |
|||
.btn-danger.disabled:active, |
|||
.btn-danger[disabled]:active, |
|||
fieldset[disabled] .btn-danger:active, |
|||
.btn-danger.disabled.active, |
|||
.btn-danger[disabled].active, |
|||
fieldset[disabled] .btn-danger.active { |
|||
background-color: #c12e2a; |
|||
background-image: none; |
|||
} |
|||
.thumbnail, |
|||
.img-thumbnail { |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); |
|||
} |
|||
.dropdown-menu > li > a:hover, |
|||
.dropdown-menu > li > a:focus { |
|||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); |
|||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
background-color: #e8e8e8; |
|||
} |
|||
.dropdown-menu > .active > a, |
|||
.dropdown-menu > .active > a:hover, |
|||
.dropdown-menu > .active > a:focus { |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
background-color: #2e6da4; |
|||
} |
|||
.navbar-default { |
|||
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); |
|||
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8)); |
|||
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
border-radius: 4px; |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); |
|||
} |
|||
.navbar-default .navbar-nav > .open > a, |
|||
.navbar-default .navbar-nav > .active > a { |
|||
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); |
|||
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); |
|||
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); |
|||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); |
|||
} |
|||
.navbar-brand, |
|||
.navbar-nav > li > a { |
|||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); |
|||
} |
|||
.navbar-inverse { |
|||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); |
|||
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); |
|||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
border-radius: 4px; |
|||
} |
|||
.navbar-inverse .navbar-nav > .open > a, |
|||
.navbar-inverse .navbar-nav > .active > a { |
|||
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); |
|||
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); |
|||
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); |
|||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); |
|||
} |
|||
.navbar-inverse .navbar-brand, |
|||
.navbar-inverse .navbar-nav > li > a { |
|||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); |
|||
} |
|||
.navbar-static-top, |
|||
.navbar-fixed-top, |
|||
.navbar-fixed-bottom { |
|||
border-radius: 0; |
|||
} |
|||
@media (max-width: 767px) { |
|||
.navbar .navbar-nav .open .dropdown-menu > .active > a, |
|||
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover, |
|||
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus { |
|||
color: #fff; |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
} |
|||
.alert { |
|||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); |
|||
} |
|||
.alert-success { |
|||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); |
|||
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); |
|||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #b2dba1; |
|||
} |
|||
.alert-info { |
|||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); |
|||
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); |
|||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #9acfea; |
|||
} |
|||
.alert-warning { |
|||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); |
|||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); |
|||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #f5e79e; |
|||
} |
|||
.alert-danger { |
|||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); |
|||
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); |
|||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #dca7a7; |
|||
} |
|||
.progress { |
|||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); |
|||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); |
|||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar { |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-success { |
|||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); |
|||
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); |
|||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-info { |
|||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); |
|||
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); |
|||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-warning { |
|||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); |
|||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); |
|||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-danger { |
|||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); |
|||
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); |
|||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-striped { |
|||
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); |
|||
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); |
|||
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); |
|||
} |
|||
.list-group { |
|||
border-radius: 4px; |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); |
|||
} |
|||
.list-group-item.active, |
|||
.list-group-item.active:hover, |
|||
.list-group-item.active:focus { |
|||
text-shadow: 0 -1px 0 #286090; |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #2b669a; |
|||
} |
|||
.list-group-item.active .badge, |
|||
.list-group-item.active:hover .badge, |
|||
.list-group-item.active:focus .badge { |
|||
text-shadow: none; |
|||
} |
|||
.panel { |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); |
|||
} |
|||
.panel-default > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); |
|||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-primary > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-success > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); |
|||
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); |
|||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-info > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); |
|||
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); |
|||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-warning > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); |
|||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); |
|||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-danger > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); |
|||
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); |
|||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.well { |
|||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); |
|||
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); |
|||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #dcdcdc; |
|||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); |
|||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); |
|||
} |
|||
/*# sourceMappingURL=bootstrap-theme.css.map */ |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,485 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using Microsoft.AspNet.Identity; |
|||
using Microsoft.AspNet.Identity.Owin; |
|||
using Microsoft.Owin.Security; |
|||
using OpenIddict.Sandbox.AspNet.Server.Models; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Controllers |
|||
{ |
|||
[Authorize] |
|||
public class AccountController : Controller |
|||
{ |
|||
private ApplicationSignInManager _signInManager; |
|||
private ApplicationUserManager _userManager; |
|||
|
|||
public AccountController() |
|||
{ |
|||
} |
|||
|
|||
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager ) |
|||
{ |
|||
UserManager = userManager; |
|||
SignInManager = signInManager; |
|||
} |
|||
|
|||
public ApplicationSignInManager SignInManager |
|||
{ |
|||
get |
|||
{ |
|||
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); |
|||
} |
|||
private set |
|||
{ |
|||
_signInManager = value; |
|||
} |
|||
} |
|||
|
|||
public ApplicationUserManager UserManager |
|||
{ |
|||
get |
|||
{ |
|||
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); |
|||
} |
|||
private set |
|||
{ |
|||
_userManager = value; |
|||
} |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/Login
|
|||
[AllowAnonymous] |
|||
public ActionResult Login(string returnUrl) |
|||
{ |
|||
ViewBag.ReturnUrl = returnUrl; |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/Login
|
|||
[HttpPost] |
|||
[AllowAnonymous] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) |
|||
{ |
|||
if (!ModelState.IsValid) |
|||
{ |
|||
return View(model); |
|||
} |
|||
|
|||
// Ceci ne comptabilise pas les échecs de connexion pour le verrouillage du compte
|
|||
// Pour que les échecs de mot de passe déclenchent le verrouillage du compte, utilisez shouldLockout: true
|
|||
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false); |
|||
switch (result) |
|||
{ |
|||
case SignInStatus.Success: |
|||
return RedirectToLocal(returnUrl); |
|||
case SignInStatus.LockedOut: |
|||
return View("Lockout"); |
|||
case SignInStatus.RequiresVerification: |
|||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); |
|||
case SignInStatus.Failure: |
|||
default: |
|||
ModelState.AddModelError("", "Tentative de connexion non valide."); |
|||
return View(model); |
|||
} |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/VerifyCode
|
|||
[AllowAnonymous] |
|||
public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe) |
|||
{ |
|||
// Nécessiter que l'utilisateur soit déjà connecté via un nom d'utilisateur/mot de passe ou une connexte externe
|
|||
if (!await SignInManager.HasBeenVerifiedAsync()) |
|||
{ |
|||
return View("Error"); |
|||
} |
|||
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/VerifyCode
|
|||
[HttpPost] |
|||
[AllowAnonymous] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) |
|||
{ |
|||
if (!ModelState.IsValid) |
|||
{ |
|||
return View(model); |
|||
} |
|||
|
|||
// Le code suivant protège des attaques par force brute contre les codes à 2 facteurs.
|
|||
// Si un utilisateur entre des codes incorrects pendant un certain intervalle, le compte de cet utilisateur
|
|||
// est alors verrouillé pendant une durée spécifiée.
|
|||
// Vous pouvez configurer les paramètres de verrouillage du compte dans IdentityConfig
|
|||
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser); |
|||
switch (result) |
|||
{ |
|||
case SignInStatus.Success: |
|||
return RedirectToLocal(model.ReturnUrl); |
|||
case SignInStatus.LockedOut: |
|||
return View("Lockout"); |
|||
case SignInStatus.Failure: |
|||
default: |
|||
ModelState.AddModelError("", "Code non valide."); |
|||
return View(model); |
|||
} |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/Register
|
|||
[AllowAnonymous] |
|||
public ActionResult Register() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/Register
|
|||
[HttpPost] |
|||
[AllowAnonymous] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> Register(RegisterViewModel model) |
|||
{ |
|||
if (ModelState.IsValid) |
|||
{ |
|||
var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; |
|||
var result = await UserManager.CreateAsync(user, model.Password); |
|||
if (result.Succeeded) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false); |
|||
|
|||
// Pour plus d'informations sur l'activation de la confirmation de compte et de la réinitialisation de mot de passe, visitez https://go.microsoft.com/fwlink/?LinkID=320771
|
|||
// Envoyer un e-mail avec ce lien
|
|||
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
|
|||
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
|
|||
// await UserManager.SendEmailAsync(user.Id, "Confirmer votre compte", "Confirmez votre compte en cliquant <a href=\"" + callbackUrl + "\">ici</a>");
|
|||
|
|||
return RedirectToAction("Index", "Home"); |
|||
} |
|||
AddErrors(result); |
|||
} |
|||
|
|||
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
|
|||
return View(model); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/ConfirmEmail
|
|||
[AllowAnonymous] |
|||
public async Task<ActionResult> ConfirmEmail(string userId, string code) |
|||
{ |
|||
if (userId == null || code == null) |
|||
{ |
|||
return View("Error"); |
|||
} |
|||
var result = await UserManager.ConfirmEmailAsync(userId, code); |
|||
return View(result.Succeeded ? "ConfirmEmail" : "Error"); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/ForgotPassword
|
|||
[AllowAnonymous] |
|||
public ActionResult ForgotPassword() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/ForgotPassword
|
|||
[HttpPost] |
|||
[AllowAnonymous] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model) |
|||
{ |
|||
if (ModelState.IsValid) |
|||
{ |
|||
var user = await UserManager.FindByNameAsync(model.Email); |
|||
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id))) |
|||
{ |
|||
// Ne révélez pas que l'utilisateur n'existe pas ou qu'il n'est pas confirmé
|
|||
return View("ForgotPasswordConfirmation"); |
|||
} |
|||
|
|||
// Pour plus d'informations sur l'activation de la confirmation de compte et de la réinitialisation de mot de passe, visitez https://go.microsoft.com/fwlink/?LinkID=320771
|
|||
// Envoyer un e-mail avec ce lien
|
|||
// string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
|
|||
// var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
|
|||
// await UserManager.SendEmailAsync(user.Id, "Réinitialiser le mot de passe", "Réinitialisez votre mot de passe en cliquant <a href=\"" + callbackUrl + "\">ici</a>");
|
|||
// return RedirectToAction("ForgotPasswordConfirmation", "Account");
|
|||
} |
|||
|
|||
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
|
|||
return View(model); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/ForgotPasswordConfirmation
|
|||
[AllowAnonymous] |
|||
public ActionResult ForgotPasswordConfirmation() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/ResetPassword
|
|||
[AllowAnonymous] |
|||
public ActionResult ResetPassword(string code) |
|||
{ |
|||
return code == null ? View("Error") : View(); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/ResetPassword
|
|||
[HttpPost] |
|||
[AllowAnonymous] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model) |
|||
{ |
|||
if (!ModelState.IsValid) |
|||
{ |
|||
return View(model); |
|||
} |
|||
var user = await UserManager.FindByNameAsync(model.Email); |
|||
if (user == null) |
|||
{ |
|||
// Ne révélez pas que l'utilisateur n'existe pas
|
|||
return RedirectToAction("ResetPasswordConfirmation", "Account"); |
|||
} |
|||
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password); |
|||
if (result.Succeeded) |
|||
{ |
|||
return RedirectToAction("ResetPasswordConfirmation", "Account"); |
|||
} |
|||
AddErrors(result); |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/ResetPasswordConfirmation
|
|||
[AllowAnonymous] |
|||
public ActionResult ResetPasswordConfirmation() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/ExternalLogin
|
|||
[HttpPost] |
|||
[AllowAnonymous] |
|||
[ValidateAntiForgeryToken] |
|||
public ActionResult ExternalLogin(string provider, string returnUrl) |
|||
{ |
|||
// Demander une redirection vers le fournisseur de connexion externe
|
|||
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl })); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/SendCode
|
|||
[AllowAnonymous] |
|||
public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe) |
|||
{ |
|||
var userId = await SignInManager.GetVerifiedUserIdAsync(); |
|||
if (userId == null) |
|||
{ |
|||
return View("Error"); |
|||
} |
|||
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId); |
|||
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList(); |
|||
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe }); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/SendCode
|
|||
[HttpPost] |
|||
[AllowAnonymous] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> SendCode(SendCodeViewModel model) |
|||
{ |
|||
if (!ModelState.IsValid) |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
// Générer le jeton et l'envoyer
|
|||
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider)) |
|||
{ |
|||
return View("Error"); |
|||
} |
|||
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/ExternalLoginCallback
|
|||
[AllowAnonymous] |
|||
public async Task<ActionResult> ExternalLoginCallback(string returnUrl) |
|||
{ |
|||
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); |
|||
if (loginInfo == null) |
|||
{ |
|||
return RedirectToAction("Login"); |
|||
} |
|||
|
|||
// Connecter cet utilisateur à ce fournisseur de connexion externe si l'utilisateur possède déjà une connexion
|
|||
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false); |
|||
switch (result) |
|||
{ |
|||
case SignInStatus.Success: |
|||
return RedirectToLocal(returnUrl); |
|||
case SignInStatus.LockedOut: |
|||
return View("Lockout"); |
|||
case SignInStatus.RequiresVerification: |
|||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false }); |
|||
case SignInStatus.Failure: |
|||
default: |
|||
// Si l'utilisateur n'a pas de compte, invitez alors celui-ci à créer un compte
|
|||
ViewBag.ReturnUrl = returnUrl; |
|||
ViewBag.LoginProvider = loginInfo.Login.LoginProvider; |
|||
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email }); |
|||
} |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/ExternalLoginConfirmation
|
|||
[HttpPost] |
|||
[AllowAnonymous] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl) |
|||
{ |
|||
if (User.Identity.IsAuthenticated) |
|||
{ |
|||
return RedirectToAction("Index", "Manage"); |
|||
} |
|||
|
|||
if (ModelState.IsValid) |
|||
{ |
|||
// Obtenir des informations sur l'utilisateur à partir du fournisseur de connexion externe
|
|||
var info = await AuthenticationManager.GetExternalLoginInfoAsync(); |
|||
if (info == null) |
|||
{ |
|||
return View("ExternalLoginFailure"); |
|||
} |
|||
var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; |
|||
var result = await UserManager.CreateAsync(user); |
|||
if (result.Succeeded) |
|||
{ |
|||
result = await UserManager.AddLoginAsync(user.Id, info.Login); |
|||
if (result.Succeeded) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
|||
return RedirectToLocal(returnUrl); |
|||
} |
|||
} |
|||
AddErrors(result); |
|||
} |
|||
|
|||
ViewBag.ReturnUrl = returnUrl; |
|||
return View(model); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Account/LogOff
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public ActionResult LogOff() |
|||
{ |
|||
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); |
|||
return RedirectToAction("Index", "Home"); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Account/ExternalLoginFailure
|
|||
[AllowAnonymous] |
|||
public ActionResult ExternalLoginFailure() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
{ |
|||
if (_userManager != null) |
|||
{ |
|||
_userManager.Dispose(); |
|||
_userManager = null; |
|||
} |
|||
|
|||
if (_signInManager != null) |
|||
{ |
|||
_signInManager.Dispose(); |
|||
_signInManager = null; |
|||
} |
|||
} |
|||
|
|||
base.Dispose(disposing); |
|||
} |
|||
|
|||
#region Applications auxiliaires
|
|||
// Utilisé(e) pour la protection XSRF lors de l'ajout de connexions externes
|
|||
private const string XsrfKey = "XsrfId"; |
|||
|
|||
private IAuthenticationManager AuthenticationManager |
|||
{ |
|||
get |
|||
{ |
|||
return HttpContext.GetOwinContext().Authentication; |
|||
} |
|||
} |
|||
|
|||
private void AddErrors(IdentityResult result) |
|||
{ |
|||
foreach (var error in result.Errors) |
|||
{ |
|||
ModelState.AddModelError("", error); |
|||
} |
|||
} |
|||
|
|||
private ActionResult RedirectToLocal(string returnUrl) |
|||
{ |
|||
if (Url.IsLocalUrl(returnUrl)) |
|||
{ |
|||
return Redirect(returnUrl); |
|||
} |
|||
return RedirectToAction("Index", "Home"); |
|||
} |
|||
|
|||
internal class ChallengeResult : HttpUnauthorizedResult |
|||
{ |
|||
public ChallengeResult(string provider, string redirectUri) |
|||
: this(provider, redirectUri, null) |
|||
{ |
|||
} |
|||
|
|||
public ChallengeResult(string provider, string redirectUri, string userId) |
|||
{ |
|||
LoginProvider = provider; |
|||
RedirectUri = redirectUri; |
|||
UserId = userId; |
|||
} |
|||
|
|||
public string LoginProvider { get; set; } |
|||
public string RedirectUri { get; set; } |
|||
public string UserId { get; set; } |
|||
|
|||
public override void ExecuteResult(ControllerContext context) |
|||
{ |
|||
var properties = new AuthenticationProperties { RedirectUri = RedirectUri }; |
|||
if (UserId != null) |
|||
{ |
|||
properties.Dictionary[XsrfKey] = UserId; |
|||
} |
|||
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider); |
|||
} |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,402 @@ |
|||
/* |
|||
* 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.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using Microsoft.AspNet.Identity; |
|||
using Microsoft.AspNet.Identity.Owin; |
|||
using Microsoft.Owin.Security; |
|||
using OpenIddict.Sandbox.AspNet.Server.Helpers; |
|||
using OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.Server.Owin; |
|||
using Owin; |
|||
using static OpenIddict.Abstractions.OpenIddictConstants; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Controllers |
|||
{ |
|||
public class AuthorizationController : Controller |
|||
{ |
|||
private readonly IOpenIddictApplicationManager _applicationManager; |
|||
private readonly IOpenIddictAuthorizationManager _authorizationManager; |
|||
private readonly IOpenIddictScopeManager _scopeManager; |
|||
|
|||
public AuthorizationController( |
|||
IOpenIddictApplicationManager applicationManager, |
|||
IOpenIddictAuthorizationManager authorizationManager, |
|||
IOpenIddictScopeManager scopeManager) |
|||
{ |
|||
_applicationManager = applicationManager; |
|||
_authorizationManager = authorizationManager; |
|||
_scopeManager = scopeManager; |
|||
} |
|||
|
|||
[HttpGet, Route("~/connect/authorize")] |
|||
public async Task<ActionResult> Authorize() |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
var request = context.GetOpenIddictServerRequest() ?? |
|||
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|||
|
|||
// Retrieve the user principal stored in the authentication cookie.
|
|||
// If a max_age parameter was provided, ensure that the cookie is not too old.
|
|||
// If the user principal can't be extracted or the cookie is too old, redirect the user to the login page.
|
|||
var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie); |
|||
if (result?.Identity == null || (request.MaxAge != null && result.Properties?.IssuedUtc != null && |
|||
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) |
|||
{ |
|||
context.Authentication.Challenge(DefaultAuthenticationTypes.ApplicationCookie); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
// Retrieve the profile of the logged in user.
|
|||
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(result.Identity.GetUserId()) ?? |
|||
throw new InvalidOperationException("The user details cannot be retrieved."); |
|||
|
|||
// Retrieve the application details from the database.
|
|||
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? |
|||
throw new InvalidOperationException("Details concerning the calling client application cannot be found."); |
|||
|
|||
// Retrieve the permanent authorizations associated with the user and the calling client application.
|
|||
var authorizations = await _authorizationManager.FindAsync( |
|||
subject: user.Id, |
|||
client : await _applicationManager.GetIdAsync(application), |
|||
status : Statuses.Valid, |
|||
type : AuthorizationTypes.Permanent, |
|||
scopes : request.GetScopes()).ToListAsync(); |
|||
|
|||
switch (await _applicationManager.GetConsentTypeAsync(application)) |
|||
{ |
|||
// If the consent is external (e.g when authorizations are granted by a sysadmin),
|
|||
// immediately return an error if no authorization can be found in the database.
|
|||
case ConsentTypes.External when !authorizations.Any(): |
|||
context.Authentication.Challenge( |
|||
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType, |
|||
properties: new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
[OpenIddictServerOwinConstants.Properties.Error] = Errors.ConsentRequired, |
|||
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = |
|||
"The logged in user is not allowed to access this client application." |
|||
})); |
|||
|
|||
return new EmptyResult(); |
|||
|
|||
// If the consent is implicit or if an authorization was found,
|
|||
// return an authorization response without displaying the consent form.
|
|||
case ConsentTypes.Implicit: |
|||
case ConsentTypes.External when authorizations.Any(): |
|||
case ConsentTypes.Explicit when authorizations.Any() && !request.HasPrompt(Prompts.Consent): |
|||
var identity = new ClaimsIdentity(OpenIddictServerOwinDefaults.AuthenticationType); |
|||
identity.AddClaims((await context.Get<ApplicationSignInManager>().CreateUserIdentityAsync(user)).Claims); |
|||
|
|||
identity.AddClaim(new Claim(Claims.Subject, identity.FindFirstValue(ClaimTypes.NameIdentifier))); |
|||
identity.AddClaim(new Claim(Claims.Name, identity.FindFirstValue(ClaimTypes.Name))); |
|||
|
|||
var principal = new ClaimsPrincipal(identity); |
|||
|
|||
// Note: in this sample, the granted scopes match the requested scope
|
|||
// but you may want to allow the user to uncheck specific scopes.
|
|||
// For that, simply restrict the list of scopes before calling SetScopes.
|
|||
principal.SetScopes(request.GetScopes()); |
|||
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|||
|
|||
// Automatically create a permanent authorization to avoid requiring explicit consent
|
|||
// for future authorization or token requests containing the same scopes.
|
|||
var authorization = authorizations.LastOrDefault(); |
|||
if (authorization == null) |
|||
{ |
|||
authorization = await _authorizationManager.CreateAsync( |
|||
principal: principal, |
|||
subject : user.Id, |
|||
client : await _applicationManager.GetIdAsync(application), |
|||
type : AuthorizationTypes.Permanent, |
|||
scopes : principal.GetScopes()); |
|||
} |
|||
|
|||
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); |
|||
|
|||
foreach (var claim in principal.Claims) |
|||
{ |
|||
claim.SetDestinations(GetDestinations(claim, principal)); |
|||
} |
|||
|
|||
context.Authentication.SignIn(new AuthenticationProperties(), (ClaimsIdentity) principal.Identity); |
|||
|
|||
return new EmptyResult(); |
|||
|
|||
// At this point, no authorization was found in the database and an error must be returned
|
|||
// if the client application specified prompt=none in the authorization request.
|
|||
case ConsentTypes.Explicit when request.HasPrompt(Prompts.None): |
|||
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None): |
|||
context.Authentication.Challenge( |
|||
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType, |
|||
properties: new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
[OpenIddictServerOwinConstants.Properties.Error] = Errors.ConsentRequired, |
|||
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = |
|||
"Interactive user consent is required." |
|||
})); |
|||
|
|||
return new EmptyResult(); |
|||
|
|||
// In every other case, render the consent form.
|
|||
default: return View(new AuthorizeViewModel |
|||
{ |
|||
ApplicationName = await _applicationManager.GetDisplayNameAsync(application), |
|||
Scope = request.Scope, |
|||
|
|||
// Flow the request parameters so they can be received by the Accept/Reject actions.
|
|||
Parameters = string.Equals(Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase) ? |
|||
from name in Request.Form.AllKeys |
|||
from value in Request.Form.GetValues(name) |
|||
select new KeyValuePair<string, string>(name, value) : |
|||
from name in Request.QueryString.AllKeys |
|||
from value in Request.QueryString.GetValues(name) |
|||
select new KeyValuePair<string, string>(name, value) |
|||
}); |
|||
} |
|||
} |
|||
|
|||
[Authorize, FormValueRequired("submit.Accept")] |
|||
[HttpPost, Route("~/connect/authorize"), ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> Accept() |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
var request = context.GetOpenIddictServerRequest() ?? |
|||
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|||
|
|||
// Retrieve the user principal stored in the authentication cookie.
|
|||
var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie); |
|||
if (result == null || result.Identity == null) |
|||
{ |
|||
context.Authentication.Challenge(DefaultAuthenticationTypes.ApplicationCookie); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
// Retrieve the profile of the logged in user.
|
|||
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(result.Identity.GetUserId()) ?? |
|||
throw new InvalidOperationException("The user details cannot be retrieved."); |
|||
|
|||
// Retrieve the application details from the database.
|
|||
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? |
|||
throw new InvalidOperationException("Details concerning the calling client application cannot be found."); |
|||
|
|||
// Retrieve the permanent authorizations associated with the user and the calling client application.
|
|||
var authorizations = await _authorizationManager.FindAsync( |
|||
subject: user.Id, |
|||
client : await _applicationManager.GetIdAsync(application), |
|||
status : Statuses.Valid, |
|||
type : AuthorizationTypes.Permanent, |
|||
scopes : request.GetScopes()).ToListAsync(); |
|||
|
|||
// Note: the same check is already made in the other action but is repeated
|
|||
// here to ensure a malicious user can't abuse this POST-only endpoint and
|
|||
// force it to return a valid response without the external authorization.
|
|||
if (!authorizations.Any() && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External)) |
|||
{ |
|||
context.Authentication.Challenge( |
|||
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType, |
|||
properties: new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
[OpenIddictServerOwinConstants.Properties.Error] = Errors.ConsentRequired, |
|||
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = |
|||
"The logged in user is not allowed to access this client application." |
|||
})); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
var identity = new ClaimsIdentity(OpenIddictServerOwinDefaults.AuthenticationType); |
|||
identity.AddClaims((await context.Get<ApplicationSignInManager>().CreateUserIdentityAsync(user)).Claims); |
|||
|
|||
identity.AddClaim(new Claim(Claims.Subject, identity.FindFirstValue(ClaimTypes.NameIdentifier))); |
|||
identity.AddClaim(new Claim(Claims.Name, identity.FindFirstValue(ClaimTypes.Name))); |
|||
|
|||
var principal = new ClaimsPrincipal(identity); |
|||
|
|||
// Note: in this sample, the granted scopes match the requested scope
|
|||
// but you may want to allow the user to uncheck specific scopes.
|
|||
// For that, simply restrict the list of scopes before calling SetScopes.
|
|||
principal.SetScopes(request.GetScopes()); |
|||
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|||
|
|||
// Automatically create a permanent authorization to avoid requiring explicit consent
|
|||
// for future authorization or token requests containing the same scopes.
|
|||
var authorization = authorizations.LastOrDefault(); |
|||
if (authorization == null) |
|||
{ |
|||
authorization = await _authorizationManager.CreateAsync( |
|||
principal: principal, |
|||
subject : user.Id, |
|||
client : await _applicationManager.GetIdAsync(application), |
|||
type : AuthorizationTypes.Permanent, |
|||
scopes : principal.GetScopes()); |
|||
} |
|||
|
|||
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); |
|||
|
|||
foreach (var claim in principal.Claims) |
|||
{ |
|||
claim.SetDestinations(GetDestinations(claim, principal)); |
|||
} |
|||
|
|||
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|||
context.Authentication.SignIn(new AuthenticationProperties(), (ClaimsIdentity) principal.Identity); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
[Authorize, FormValueRequired("submit.Deny")] |
|||
[HttpPost, Route("~/connect/authorize"), ValidateAntiForgeryToken] |
|||
// 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.
|
|||
public ActionResult Deny() |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
context.Authentication.Challenge(OpenIddictServerOwinDefaults.AuthenticationType); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
[HttpGet, Route("~/connect/logout")] |
|||
public ActionResult Logout() => View(new AuthorizeViewModel |
|||
{ |
|||
// Flow the request parameters so they can be received by the Accept/Reject actions.
|
|||
Parameters = string.Equals(Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase) ? |
|||
from name in Request.Form.AllKeys |
|||
from value in Request.Form.GetValues(name) |
|||
select new KeyValuePair<string, string>(name, value) : |
|||
from name in Request.QueryString.AllKeys |
|||
from value in Request.QueryString.GetValues(name) |
|||
select new KeyValuePair<string, string>(name, value) |
|||
}); |
|||
|
|||
[ActionName(nameof(Logout)), HttpPost, Route("~/connect/logout"), ValidateAntiForgeryToken] |
|||
public ActionResult LogoutPost() |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
context.Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie); |
|||
|
|||
context.Authentication.SignOut( |
|||
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType, |
|||
properties: new AuthenticationProperties |
|||
{ |
|||
RedirectUri = "/" |
|||
}); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
[HttpPost, Route("~/connect/token")] |
|||
public async Task<ActionResult> Exchange() |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
var request = context.GetOpenIddictServerRequest() ?? |
|||
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|||
|
|||
if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType()) |
|||
{ |
|||
// Retrieve the claims principal stored in the authorization code/device code/refresh token.
|
|||
var principal = new ClaimsPrincipal((await context.Authentication.AuthenticateAsync(OpenIddictServerOwinDefaults.AuthenticationType)).Identity); |
|||
|
|||
// Retrieve the user profile corresponding to the authorization code/refresh token.
|
|||
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(principal.GetClaim(Claims.Subject)); |
|||
if (user == null) |
|||
{ |
|||
context.Authentication.Challenge( |
|||
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType, |
|||
properties: new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
[OpenIddictServerOwinConstants.Properties.Error] = Errors.InvalidGrant, |
|||
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = "The token is no longer valid." |
|||
})); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
// Ensure the user is still allowed to sign in.
|
|||
if (context.GetUserManager<ApplicationUserManager>().IsLockedOut(user.Id)) |
|||
{ |
|||
context.Authentication.Challenge( |
|||
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType, |
|||
properties: new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
[OpenIddictServerOwinConstants.Properties.Error] = Errors.InvalidGrant, |
|||
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." |
|||
})); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
var identity = new ClaimsIdentity(OpenIddictServerOwinDefaults.AuthenticationType); |
|||
identity.AddClaims((await context.Get<ApplicationSignInManager>().CreateUserIdentityAsync(user)).Claims); |
|||
|
|||
identity.AddClaim(new Claim(Claims.Subject, identity.FindFirstValue(ClaimTypes.NameIdentifier))); |
|||
identity.AddClaim(new Claim(Claims.Name, identity.FindFirstValue(ClaimTypes.Name))); |
|||
|
|||
foreach (var claim in identity.Claims) |
|||
{ |
|||
claim.SetDestinations(GetDestinations(claim, principal)); |
|||
} |
|||
|
|||
// Ask OpenIddict to issue the appropriate access/identity tokens.
|
|||
context.Authentication.SignIn(new AuthenticationProperties(), identity); |
|||
|
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
throw new InvalidOperationException("The specified grant type is not supported."); |
|||
} |
|||
|
|||
private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal) |
|||
{ |
|||
// Note: by default, claims are NOT automatically included in the access and identity tokens.
|
|||
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
|
|||
// whether they should be included in access tokens, in identity tokens or in both.
|
|||
|
|||
switch (claim.Type) |
|||
{ |
|||
case Claims.Name: |
|||
yield return Destinations.AccessToken; |
|||
|
|||
if (principal.HasScope(Scopes.Profile)) |
|||
yield return Destinations.IdentityToken; |
|||
|
|||
yield break; |
|||
|
|||
case Claims.Email: |
|||
yield return Destinations.AccessToken; |
|||
|
|||
if (principal.HasScope(Scopes.Email)) |
|||
yield return Destinations.IdentityToken; |
|||
|
|||
yield break; |
|||
|
|||
case Claims.Role: |
|||
yield return Destinations.AccessToken; |
|||
|
|||
if (principal.HasScope(Scopes.Roles)) |
|||
yield return Destinations.IdentityToken; |
|||
|
|||
yield break; |
|||
|
|||
// Never include the security stamp in the access and identity tokens, as it's a secret value.
|
|||
case "AspNet.Identity.SecurityStamp": yield break; |
|||
|
|||
default: |
|||
yield return Destinations.AccessToken; |
|||
yield break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Controllers |
|||
{ |
|||
public class HomeController : Controller |
|||
{ |
|||
public ActionResult Index() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
public ActionResult About() |
|||
{ |
|||
ViewBag.Message = "Your application description page."; |
|||
|
|||
return View(); |
|||
} |
|||
|
|||
public ActionResult Contact() |
|||
{ |
|||
ViewBag.Message = "Your contact page."; |
|||
|
|||
return View(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,389 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using Microsoft.AspNet.Identity; |
|||
using Microsoft.AspNet.Identity.Owin; |
|||
using Microsoft.Owin.Security; |
|||
using OpenIddict.Sandbox.AspNet.Server.Models; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Controllers |
|||
{ |
|||
[Authorize] |
|||
public class ManageController : Controller |
|||
{ |
|||
private ApplicationSignInManager _signInManager; |
|||
private ApplicationUserManager _userManager; |
|||
|
|||
public ManageController() |
|||
{ |
|||
} |
|||
|
|||
public ManageController(ApplicationUserManager userManager, ApplicationSignInManager signInManager) |
|||
{ |
|||
UserManager = userManager; |
|||
SignInManager = signInManager; |
|||
} |
|||
|
|||
public ApplicationSignInManager SignInManager |
|||
{ |
|||
get |
|||
{ |
|||
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); |
|||
} |
|||
private set |
|||
{ |
|||
_signInManager = value; |
|||
} |
|||
} |
|||
|
|||
public ApplicationUserManager UserManager |
|||
{ |
|||
get |
|||
{ |
|||
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); |
|||
} |
|||
private set |
|||
{ |
|||
_userManager = value; |
|||
} |
|||
} |
|||
|
|||
//
|
|||
// GET: /Manage/Index
|
|||
public async Task<ActionResult> Index(ManageMessageId? message) |
|||
{ |
|||
ViewBag.StatusMessage = |
|||
message == ManageMessageId.ChangePasswordSuccess ? "Votre mot de passe a été changé." |
|||
: message == ManageMessageId.SetPasswordSuccess ? "Votre mot de passe a été défini." |
|||
: message == ManageMessageId.SetTwoFactorSuccess ? "Votre fournisseur d'authentification à 2 facteurs a été défini." |
|||
: message == ManageMessageId.Error ? "Une erreur s'est produite." |
|||
: message == ManageMessageId.AddPhoneSuccess ? "Votre numéro de téléphone a été ajouté." |
|||
: message == ManageMessageId.RemovePhoneSuccess ? "Votre numéro de téléphone a été supprimé." |
|||
: ""; |
|||
|
|||
var userId = User.Identity.GetUserId(); |
|||
var model = new IndexViewModel |
|||
{ |
|||
HasPassword = HasPassword(), |
|||
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId), |
|||
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId), |
|||
Logins = await UserManager.GetLoginsAsync(userId), |
|||
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId) |
|||
}; |
|||
return View(model); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/RemoveLogin
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> RemoveLogin(string loginProvider, string providerKey) |
|||
{ |
|||
ManageMessageId? message; |
|||
var result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey)); |
|||
if (result.Succeeded) |
|||
{ |
|||
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
|||
} |
|||
message = ManageMessageId.RemoveLoginSuccess; |
|||
} |
|||
else |
|||
{ |
|||
message = ManageMessageId.Error; |
|||
} |
|||
return RedirectToAction("ManageLogins", new { Message = message }); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Manage/AddPhoneNumber
|
|||
public ActionResult AddPhoneNumber() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/AddPhoneNumber
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model) |
|||
{ |
|||
if (!ModelState.IsValid) |
|||
{ |
|||
return View(model); |
|||
} |
|||
// Générer le jeton et l'envoyer
|
|||
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), model.Number); |
|||
if (UserManager.SmsService != null) |
|||
{ |
|||
var message = new IdentityMessage |
|||
{ |
|||
Destination = model.Number, |
|||
Body = "Votre code de sécurité est : " + code |
|||
}; |
|||
await UserManager.SmsService.SendAsync(message); |
|||
} |
|||
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number }); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/EnableTwoFactorAuthentication
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> EnableTwoFactorAuthentication() |
|||
{ |
|||
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true); |
|||
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
|||
} |
|||
return RedirectToAction("Index", "Manage"); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/DisableTwoFactorAuthentication
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> DisableTwoFactorAuthentication() |
|||
{ |
|||
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), false); |
|||
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
|||
} |
|||
return RedirectToAction("Index", "Manage"); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Manage/VerifyPhoneNumber
|
|||
public async Task<ActionResult> VerifyPhoneNumber(string phoneNumber) |
|||
{ |
|||
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), phoneNumber); |
|||
// Envoyer un SMS via le fournisseur SMS afin de vérifier le numéro de téléphone
|
|||
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/VerifyPhoneNumber
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model) |
|||
{ |
|||
if (!ModelState.IsValid) |
|||
{ |
|||
return View(model); |
|||
} |
|||
var result = await UserManager.ChangePhoneNumberAsync(User.Identity.GetUserId(), model.PhoneNumber, model.Code); |
|||
if (result.Succeeded) |
|||
{ |
|||
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
|||
} |
|||
return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess }); |
|||
} |
|||
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
|
|||
ModelState.AddModelError("", "La vérification du téléphone a échoué"); |
|||
return View(model); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/RemovePhoneNumber
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> RemovePhoneNumber() |
|||
{ |
|||
var result = await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId(), null); |
|||
if (!result.Succeeded) |
|||
{ |
|||
return RedirectToAction("Index", new { Message = ManageMessageId.Error }); |
|||
} |
|||
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
|||
} |
|||
return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess }); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Manage/ChangePassword
|
|||
public ActionResult ChangePassword() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/ChangePassword
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model) |
|||
{ |
|||
if (!ModelState.IsValid) |
|||
{ |
|||
return View(model); |
|||
} |
|||
var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword); |
|||
if (result.Succeeded) |
|||
{ |
|||
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
|||
} |
|||
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess }); |
|||
} |
|||
AddErrors(result); |
|||
return View(model); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Manage/SetPassword
|
|||
public ActionResult SetPassword() |
|||
{ |
|||
return View(); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/SetPassword
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public async Task<ActionResult> SetPassword(SetPasswordViewModel model) |
|||
{ |
|||
if (ModelState.IsValid) |
|||
{ |
|||
var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword); |
|||
if (result.Succeeded) |
|||
{ |
|||
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
|||
} |
|||
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess }); |
|||
} |
|||
AddErrors(result); |
|||
} |
|||
|
|||
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
|
|||
return View(model); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Manage/ManageLogins
|
|||
public async Task<ActionResult> ManageLogins(ManageMessageId? message) |
|||
{ |
|||
ViewBag.StatusMessage = |
|||
message == ManageMessageId.RemoveLoginSuccess ? "La connexion externe a été supprimée." |
|||
: message == ManageMessageId.Error ? "Une erreur s'est produite." |
|||
: ""; |
|||
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId()); |
|||
if (user == null) |
|||
{ |
|||
return View("Error"); |
|||
} |
|||
var userLogins = await UserManager.GetLoginsAsync(User.Identity.GetUserId()); |
|||
var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList(); |
|||
ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1; |
|||
return View(new ManageLoginsViewModel |
|||
{ |
|||
CurrentLogins = userLogins, |
|||
OtherLogins = otherLogins |
|||
}); |
|||
} |
|||
|
|||
//
|
|||
// POST: /Manage/LinkLogin
|
|||
[HttpPost] |
|||
[ValidateAntiForgeryToken] |
|||
public ActionResult LinkLogin(string provider) |
|||
{ |
|||
// Demander une redirection vers le fournisseur de connexion externe afin de lier une connexion pour l'utilisateur actuel
|
|||
return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId()); |
|||
} |
|||
|
|||
//
|
|||
// GET: /Manage/LinkLoginCallback
|
|||
public async Task<ActionResult> LinkLoginCallback() |
|||
{ |
|||
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId()); |
|||
if (loginInfo == null) |
|||
{ |
|||
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error }); |
|||
} |
|||
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login); |
|||
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error }); |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing && _userManager != null) |
|||
{ |
|||
_userManager.Dispose(); |
|||
_userManager = null; |
|||
} |
|||
|
|||
base.Dispose(disposing); |
|||
} |
|||
|
|||
#region Applications d'assistance
|
|||
// Utilisé(e) pour la protection XSRF lors de l'ajout de connexions externes
|
|||
private const string XsrfKey = "XsrfId"; |
|||
|
|||
private IAuthenticationManager AuthenticationManager |
|||
{ |
|||
get |
|||
{ |
|||
return HttpContext.GetOwinContext().Authentication; |
|||
} |
|||
} |
|||
|
|||
private void AddErrors(IdentityResult result) |
|||
{ |
|||
foreach (var error in result.Errors) |
|||
{ |
|||
ModelState.AddModelError("", error); |
|||
} |
|||
} |
|||
|
|||
private bool HasPassword() |
|||
{ |
|||
var user = UserManager.FindById(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
return user.PasswordHash != null; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private bool HasPhoneNumber() |
|||
{ |
|||
var user = UserManager.FindById(User.Identity.GetUserId()); |
|||
if (user != null) |
|||
{ |
|||
return user.PhoneNumber != null; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public enum ManageMessageId |
|||
{ |
|||
AddPhoneSuccess, |
|||
ChangePasswordSuccess, |
|||
SetTwoFactorSuccess, |
|||
SetPasswordSuccess, |
|||
RemoveLoginSuccess, |
|||
RemovePhoneSuccess, |
|||
Error |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using Microsoft.AspNet.Identity.Owin; |
|||
using Microsoft.Owin.Security; |
|||
using OpenIddict.Validation.Owin; |
|||
using static OpenIddict.Abstractions.OpenIddictConstants; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Controllers |
|||
{ |
|||
public class ResourceController : Controller |
|||
{ |
|||
[HttpGet, Route("~/api/message")] |
|||
public async Task<ActionResult> GetMessage() |
|||
{ |
|||
var context = HttpContext.GetOwinContext(); |
|||
|
|||
var result = await context.Authentication.AuthenticateAsync(OpenIddictValidationOwinDefaults.AuthenticationType); |
|||
if (result is null) |
|||
{ |
|||
context.Authentication.Challenge(OpenIddictValidationOwinDefaults.AuthenticationType); |
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
// This demo action requires that the client application be granted the "demo_api" scope.
|
|||
// If it was not granted, a detailed error is returned to the client application to inform it
|
|||
// that the authorization process must be restarted with the specified scope to access this API.
|
|||
if (!result.Identity.HasClaim(Claims.Private.Scope, "demo_api")) |
|||
{ |
|||
context.Authentication.Challenge( |
|||
authenticationTypes: OpenIddictValidationOwinDefaults.AuthenticationType, |
|||
properties: new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
[OpenIddictValidationOwinConstants.Properties.Scope] = "demo_api", |
|||
[OpenIddictValidationOwinConstants.Properties.Error] = Errors.InsufficientScope, |
|||
[OpenIddictValidationOwinConstants.Properties.ErrorDescription] = |
|||
"The 'demo_api' scope is required to perform this action." |
|||
})); |
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
var user = await context.GetUserManager<ApplicationUserManager>() |
|||
.FindByIdAsync(result.Identity.FindFirst(Claims.Subject).Value); |
|||
|
|||
if (user is null) |
|||
{ |
|||
context.Authentication.Challenge( |
|||
authenticationTypes: OpenIddictValidationOwinDefaults.AuthenticationType, |
|||
properties: new AuthenticationProperties(new Dictionary<string, string> |
|||
{ |
|||
[OpenIddictValidationOwinConstants.Properties.Error] = Errors.InvalidToken, |
|||
[OpenIddictValidationOwinConstants.Properties.ErrorDescription] = |
|||
"The specified access token is bound to an account that no longer exists." |
|||
})); |
|||
return new EmptyResult(); |
|||
} |
|||
|
|||
return Content($"{user.UserName} has been successfully authenticated."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
<%@ Application Codebehind="Global.asax.cs" Inherits="OpenIddict.Sandbox.AspNet.Server.MvcApplication" Language="C#" %> |
|||
@ -0,0 +1,18 @@ |
|||
using System.Web; |
|||
using System.Web.Mvc; |
|||
using System.Web.Optimization; |
|||
using System.Web.Routing; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server |
|||
{ |
|||
public class MvcApplication : HttpApplication |
|||
{ |
|||
protected void Application_Start() |
|||
{ |
|||
AreaRegistration.RegisterAllAreas(); |
|||
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); |
|||
RouteConfig.RegisterRoutes(RouteTable.Routes); |
|||
BundleConfig.RegisterBundles(BundleTable.Bundles); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Helpers |
|||
{ |
|||
public static class AsyncEnumerableExtensions |
|||
{ |
|||
public static Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> source) |
|||
{ |
|||
if (source == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(source)); |
|||
} |
|||
|
|||
return ExecuteAsync(); |
|||
|
|||
async Task<List<T>> ExecuteAsync() |
|||
{ |
|||
var list = new List<T>(); |
|||
|
|||
await foreach (var element in source) |
|||
{ |
|||
list.Add(element); |
|||
} |
|||
|
|||
return list; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using System.Web.Mvc; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Helpers |
|||
{ |
|||
public sealed class FormValueRequiredAttribute : ActionMethodSelectorAttribute |
|||
{ |
|||
private readonly string _name; |
|||
|
|||
public FormValueRequiredAttribute(string name) |
|||
{ |
|||
_name = name; |
|||
} |
|||
|
|||
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) |
|||
{ |
|||
if (string.Equals(controllerContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase) || |
|||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) || |
|||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase) || |
|||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "TRACE", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(controllerContext.HttpContext.Request.ContentType)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return !string.IsNullOrEmpty(controllerContext.HttpContext.Request.Form[_name]); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Models |
|||
{ |
|||
public class ExternalLoginConfirmationViewModel |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Courrier électronique")] |
|||
public string Email { get; set; } |
|||
} |
|||
|
|||
public class ExternalLoginListViewModel |
|||
{ |
|||
public string ReturnUrl { get; set; } |
|||
} |
|||
|
|||
public class SendCodeViewModel |
|||
{ |
|||
public string SelectedProvider { get; set; } |
|||
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; } |
|||
public string ReturnUrl { get; set; } |
|||
public bool RememberMe { get; set; } |
|||
} |
|||
|
|||
public class VerifyCodeViewModel |
|||
{ |
|||
[Required] |
|||
public string Provider { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Code")] |
|||
public string Code { get; set; } |
|||
public string ReturnUrl { get; set; } |
|||
|
|||
[Display(Name = "Mémoriser ce navigateur ?")] |
|||
public bool RememberBrowser { get; set; } |
|||
|
|||
public bool RememberMe { get; set; } |
|||
} |
|||
|
|||
public class ForgotViewModel |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Courrier électronique")] |
|||
public string Email { get; set; } |
|||
} |
|||
|
|||
public class LoginViewModel |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Courrier électronique")] |
|||
[EmailAddress] |
|||
public string Email { get; set; } |
|||
|
|||
[Required] |
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Mot de passe")] |
|||
public string Password { get; set; } |
|||
|
|||
[Display(Name = "Mémoriser mes informations")] |
|||
public bool RememberMe { get; set; } |
|||
} |
|||
|
|||
public class RegisterViewModel |
|||
{ |
|||
[Required] |
|||
[EmailAddress] |
|||
[Display(Name = "Courrier électronique")] |
|||
public string Email { get; set; } |
|||
|
|||
[Required] |
|||
[StringLength(100, ErrorMessage = "{0} doit contenir au moins {2} caractères.", MinimumLength = 6)] |
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Mot de passe")] |
|||
public string Password { get; set; } |
|||
|
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Confirmer le mot de passe")] |
|||
[Compare("Password", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")] |
|||
public string ConfirmPassword { get; set; } |
|||
} |
|||
|
|||
public class ResetPasswordViewModel |
|||
{ |
|||
[Required] |
|||
[EmailAddress] |
|||
[Display(Name = "Courrier électronique")] |
|||
public string Email { get; set; } |
|||
|
|||
[Required] |
|||
[StringLength(100, ErrorMessage = "{0} doit contenir au moins {2} caractères.", MinimumLength = 6)] |
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Mot de passe")] |
|||
public string Password { get; set; } |
|||
|
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Confirmer le mot de passe")] |
|||
[Compare("Password", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")] |
|||
public string ConfirmPassword { get; set; } |
|||
|
|||
public string Code { get; set; } |
|||
} |
|||
|
|||
public class ForgotPasswordViewModel |
|||
{ |
|||
[Required] |
|||
[EmailAddress] |
|||
[Display(Name = "E-mail")] |
|||
public string Email { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System.Data.Entity; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNet.Identity; |
|||
using Microsoft.AspNet.Identity.EntityFramework; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Models |
|||
{ |
|||
// Vous pouvez ajouter des données de profil pour l'utilisateur en ajoutant d'autres propriétés à votre classe ApplicationUser. Pour en savoir plus, consultez https://go.microsoft.com/fwlink/?LinkID=317594.
|
|||
public class ApplicationUser : IdentityUser |
|||
{ |
|||
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager) |
|||
{ |
|||
// Notez que l'authenticationType doit correspondre à celui défini dans CookieAuthenticationOptions.AuthenticationType
|
|||
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); |
|||
// Ajouter des revendications utilisateur personnalisées ici
|
|||
return userIdentity; |
|||
} |
|||
} |
|||
|
|||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> |
|||
{ |
|||
public ApplicationDbContext() |
|||
: base("DefaultConnection", throwIfV1Schema: false) |
|||
{ |
|||
} |
|||
|
|||
public static ApplicationDbContext Create() |
|||
{ |
|||
return new ApplicationDbContext(); |
|||
} |
|||
|
|||
protected override void OnModelCreating(DbModelBuilder modelBuilder) |
|||
{ |
|||
base.OnModelCreating(modelBuilder); |
|||
|
|||
modelBuilder.UseOpenIddict(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Microsoft.AspNet.Identity; |
|||
using Microsoft.Owin.Security; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.Models |
|||
{ |
|||
public class IndexViewModel |
|||
{ |
|||
public bool HasPassword { get; set; } |
|||
public IList<UserLoginInfo> Logins { get; set; } |
|||
public string PhoneNumber { get; set; } |
|||
public bool TwoFactor { get; set; } |
|||
public bool BrowserRemembered { get; set; } |
|||
} |
|||
|
|||
public class ManageLoginsViewModel |
|||
{ |
|||
public IList<UserLoginInfo> CurrentLogins { get; set; } |
|||
public IList<AuthenticationDescription> OtherLogins { get; set; } |
|||
} |
|||
|
|||
public class FactorViewModel |
|||
{ |
|||
public string Purpose { get; set; } |
|||
} |
|||
|
|||
public class SetPasswordViewModel |
|||
{ |
|||
[Required] |
|||
[StringLength(100, ErrorMessage = "La chaîne {0} doit comporter au moins {2} caractères.", MinimumLength = 6)] |
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Nouveau mot de passe")] |
|||
public string NewPassword { get; set; } |
|||
|
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Confirmer le nouveau mot de passe")] |
|||
[Compare("NewPassword", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")] |
|||
public string ConfirmPassword { get; set; } |
|||
} |
|||
|
|||
public class ChangePasswordViewModel |
|||
{ |
|||
[Required] |
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Mot de passe actuel")] |
|||
public string OldPassword { get; set; } |
|||
|
|||
[Required] |
|||
[StringLength(100, ErrorMessage = "La chaîne {0} doit comporter au moins {2} caractères.", MinimumLength = 6)] |
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Nouveau mot de passe")] |
|||
public string NewPassword { get; set; } |
|||
|
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Confirmer le nouveau mot de passe")] |
|||
[Compare("NewPassword", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")] |
|||
public string ConfirmPassword { get; set; } |
|||
} |
|||
|
|||
public class AddPhoneNumberViewModel |
|||
{ |
|||
[Required] |
|||
[Phone] |
|||
[Display(Name = "Numéro de téléphone")] |
|||
public string Number { get; set; } |
|||
} |
|||
|
|||
public class VerifyPhoneNumberViewModel |
|||
{ |
|||
[Required] |
|||
[Display(Name = "Code")] |
|||
public string Code { get; set; } |
|||
|
|||
[Required] |
|||
[Phone] |
|||
[Display(Name = "Numéro de téléphone")] |
|||
public string PhoneNumber { get; set; } |
|||
} |
|||
|
|||
public class ConfigureTwoFactorViewModel |
|||
{ |
|||
public string SelectedProvider { get; set; } |
|||
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
<Project Sdk="MSBuild.SDK.SystemWeb/4.0.77"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net48</TargetFramework> |
|||
<AppConfig>Web.config</AppConfig> |
|||
<GeneratedBindingRedirectsAction>Overwrite</GeneratedBindingRedirectsAction> |
|||
<VSToolsPath>$(NuGetPackageRoot)msbuild.microsoft.visualstudio.web.targets\14.0.0.3\tools\VSToolsPath</VSToolsPath> |
|||
<MvcBuildViews>false</MvcBuildViews> |
|||
<ImplicitUsings>disable</ImplicitUsings> |
|||
<NoWarn>CA3147</NoWarn> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<Reference Include="System.Web.Extensions" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\OpenIddict.EntityFramework\OpenIddict.EntityFramework.csproj" /> |
|||
<ProjectReference Include="..\..\src\OpenIddict.MongoDb\OpenIddict.MongoDb.csproj" /> |
|||
<ProjectReference Include="..\..\src\OpenIddict.Server.Owin\OpenIddict.Server.Owin.csproj" /> |
|||
<ProjectReference Include="..\..\src\OpenIddict.Validation.Owin\OpenIddict.Validation.Owin.csproj" /> |
|||
<ProjectReference Include="..\..\src\OpenIddict.Validation.ServerIntegration\OpenIddict.Validation.ServerIntegration.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Antlr" /> |
|||
<PackageReference Include="Autofac.Extensions.DependencyInjection" /> |
|||
<PackageReference Include="Autofac.Mvc5" /> |
|||
<PackageReference Include="Autofac.Owin" /> |
|||
<PackageReference Include="Microsoft.AspNet.Identity.EntityFramework" /> |
|||
<PackageReference Include="Microsoft.AspNet.Identity.Owin" /> |
|||
<PackageReference Include="Microsoft.AspNet.Mvc" /> |
|||
<PackageReference Include="Microsoft.AspNet.Web.Optimization" /> |
|||
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" /> |
|||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" /> |
|||
<PackageReference Include="Microsoft.Owin.Host.SystemWeb" /> |
|||
<PackageReference Include="Microsoft.Owin.Security.Cookies" /> |
|||
<PackageReference Include="Microsoft.Owin.Security.OAuth" /> |
|||
<PackageReference Include="Microsoft.Web.Infrastructure" PrivateAssets="all" /> |
|||
<PackageReference Include="Newtonsoft.Json" /> |
|||
<PackageReference Include="WebGrease" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,19 @@ |
|||
{ |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:55946/", |
|||
"sslPort": 44349 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"IIS Express": { |
|||
"commandName": "IISExpress", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"dependencies": { |
|||
"mssql1": { |
|||
"type": "mssql", |
|||
"connectionId": "ConnectionStrings:DefaultConnection" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"dependencies": { |
|||
"mssql1": { |
|||
"type": "mssql.local", |
|||
"connectionId": "ConnectionStrings:DefaultConnection" |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
@ -0,0 +1,432 @@ |
|||
// Unobtrusive validation support library for jQuery and jQuery Validate
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|||
// @version v3.2.11
|
|||
|
|||
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */ |
|||
/*global document: false, jQuery: false */ |
|||
|
|||
(function (factory) { |
|||
if (typeof define === 'function' && define.amd) { |
|||
// AMD. Register as an anonymous module.
|
|||
define("jquery.validate.unobtrusive", ['jquery-validation'], factory); |
|||
} else if (typeof module === 'object' && module.exports) { |
|||
// CommonJS-like environments that support module.exports
|
|||
module.exports = factory(require('jquery-validation')); |
|||
} else { |
|||
// Browser global
|
|||
jQuery.validator.unobtrusive = factory(jQuery); |
|||
} |
|||
}(function ($) { |
|||
var $jQval = $.validator, |
|||
adapters, |
|||
data_validation = "unobtrusiveValidation"; |
|||
|
|||
function setValidationValues(options, ruleName, value) { |
|||
options.rules[ruleName] = value; |
|||
if (options.message) { |
|||
options.messages[ruleName] = options.message; |
|||
} |
|||
} |
|||
|
|||
function splitAndTrim(value) { |
|||
return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); |
|||
} |
|||
|
|||
function escapeAttributeValue(value) { |
|||
// As mentioned on http://api.jquery.com/category/selectors/
|
|||
return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); |
|||
} |
|||
|
|||
function getModelPrefix(fieldName) { |
|||
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); |
|||
} |
|||
|
|||
function appendModelPrefix(value, prefix) { |
|||
if (value.indexOf("*.") === 0) { |
|||
value = value.replace("*.", prefix); |
|||
} |
|||
return value; |
|||
} |
|||
|
|||
function onError(error, inputElement) { // 'this' is the form element
|
|||
var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), |
|||
replaceAttrValue = container.attr("data-valmsg-replace"), |
|||
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; |
|||
|
|||
container.removeClass("field-validation-valid").addClass("field-validation-error"); |
|||
error.data("unobtrusiveContainer", container); |
|||
|
|||
if (replace) { |
|||
container.empty(); |
|||
error.removeClass("input-validation-error").appendTo(container); |
|||
} |
|||
else { |
|||
error.hide(); |
|||
} |
|||
} |
|||
|
|||
function onErrors(event, validator) { // 'this' is the form element
|
|||
var container = $(this).find("[data-valmsg-summary=true]"), |
|||
list = container.find("ul"); |
|||
|
|||
if (list && list.length && validator.errorList.length) { |
|||
list.empty(); |
|||
container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); |
|||
|
|||
$.each(validator.errorList, function () { |
|||
$("<li />").html(this.message).appendTo(list); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
function onSuccess(error) { // 'this' is the form element
|
|||
var container = error.data("unobtrusiveContainer"); |
|||
|
|||
if (container) { |
|||
var replaceAttrValue = container.attr("data-valmsg-replace"), |
|||
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; |
|||
|
|||
container.addClass("field-validation-valid").removeClass("field-validation-error"); |
|||
error.removeData("unobtrusiveContainer"); |
|||
|
|||
if (replace) { |
|||
container.empty(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function onReset(event) { // 'this' is the form element
|
|||
var $form = $(this), |
|||
key = '__jquery_unobtrusive_validation_form_reset'; |
|||
if ($form.data(key)) { |
|||
return; |
|||
} |
|||
// Set a flag that indicates we're currently resetting the form.
|
|||
$form.data(key, true); |
|||
try { |
|||
$form.data("validator").resetForm(); |
|||
} finally { |
|||
$form.removeData(key); |
|||
} |
|||
|
|||
$form.find(".validation-summary-errors") |
|||
.addClass("validation-summary-valid") |
|||
.removeClass("validation-summary-errors"); |
|||
$form.find(".field-validation-error") |
|||
.addClass("field-validation-valid") |
|||
.removeClass("field-validation-error") |
|||
.removeData("unobtrusiveContainer") |
|||
.find(">*") // If we were using valmsg-replace, get the underlying error
|
|||
.removeData("unobtrusiveContainer"); |
|||
} |
|||
|
|||
function validationInfo(form) { |
|||
var $form = $(form), |
|||
result = $form.data(data_validation), |
|||
onResetProxy = $.proxy(onReset, form), |
|||
defaultOptions = $jQval.unobtrusive.options || {}, |
|||
execInContext = function (name, args) { |
|||
var func = defaultOptions[name]; |
|||
func && $.isFunction(func) && func.apply(form, args); |
|||
}; |
|||
|
|||
if (!result) { |
|||
result = { |
|||
options: { // options structure passed to jQuery Validate's validate() method
|
|||
errorClass: defaultOptions.errorClass || "input-validation-error", |
|||
errorElement: defaultOptions.errorElement || "span", |
|||
errorPlacement: function () { |
|||
onError.apply(form, arguments); |
|||
execInContext("errorPlacement", arguments); |
|||
}, |
|||
invalidHandler: function () { |
|||
onErrors.apply(form, arguments); |
|||
execInContext("invalidHandler", arguments); |
|||
}, |
|||
messages: {}, |
|||
rules: {}, |
|||
success: function () { |
|||
onSuccess.apply(form, arguments); |
|||
execInContext("success", arguments); |
|||
} |
|||
}, |
|||
attachValidation: function () { |
|||
$form |
|||
.off("reset." + data_validation, onResetProxy) |
|||
.on("reset." + data_validation, onResetProxy) |
|||
.validate(this.options); |
|||
}, |
|||
validate: function () { // a validation function that is called by unobtrusive Ajax
|
|||
$form.validate(); |
|||
return $form.valid(); |
|||
} |
|||
}; |
|||
$form.data(data_validation, result); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
$jQval.unobtrusive = { |
|||
adapters: [], |
|||
|
|||
parseElement: function (element, skipAttach) { |
|||
/// <summary>
|
|||
/// Parses a single HTML element for unobtrusive validation attributes.
|
|||
/// </summary>
|
|||
/// <param name="element" domElement="true">The HTML element to be parsed.</param>
|
|||
/// <param name="skipAttach" type="Boolean">[Optional] true to skip attaching the
|
|||
/// validation to the form. If parsing just this single element, you should specify true.
|
|||
/// If parsing several elements, you should specify false, and manually attach the validation
|
|||
/// to the form when you are finished. The default is false.</param>
|
|||
var $element = $(element), |
|||
form = $element.parents("form")[0], |
|||
valInfo, rules, messages; |
|||
|
|||
if (!form) { // Cannot do client-side validation without a form
|
|||
return; |
|||
} |
|||
|
|||
valInfo = validationInfo(form); |
|||
valInfo.options.rules[element.name] = rules = {}; |
|||
valInfo.options.messages[element.name] = messages = {}; |
|||
|
|||
$.each(this.adapters, function () { |
|||
var prefix = "data-val-" + this.name, |
|||
message = $element.attr(prefix), |
|||
paramValues = {}; |
|||
|
|||
if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy)
|
|||
prefix += "-"; |
|||
|
|||
$.each(this.params, function () { |
|||
paramValues[this] = $element.attr(prefix + this); |
|||
}); |
|||
|
|||
this.adapt({ |
|||
element: element, |
|||
form: form, |
|||
message: message, |
|||
params: paramValues, |
|||
rules: rules, |
|||
messages: messages |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
$.extend(rules, { "__dummy__": true }); |
|||
|
|||
if (!skipAttach) { |
|||
valInfo.attachValidation(); |
|||
} |
|||
}, |
|||
|
|||
parse: function (selector) { |
|||
/// <summary>
|
|||
/// Parses all the HTML elements in the specified selector. It looks for input elements decorated
|
|||
/// with the [data-val=true] attribute value and enables validation according to the data-val-*
|
|||
/// attribute values.
|
|||
/// </summary>
|
|||
/// <param name="selector" type="String">Any valid jQuery selector.</param>
|
|||
|
|||
// $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one
|
|||
// element with data-val=true
|
|||
var $selector = $(selector), |
|||
$forms = $selector.parents() |
|||
.addBack() |
|||
.filter("form") |
|||
.add($selector.find("form")) |
|||
.has("[data-val=true]"); |
|||
|
|||
$selector.find("[data-val=true]").each(function () { |
|||
$jQval.unobtrusive.parseElement(this, true); |
|||
}); |
|||
|
|||
$forms.each(function () { |
|||
var info = validationInfo(this); |
|||
if (info) { |
|||
info.attachValidation(); |
|||
} |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
adapters = $jQval.unobtrusive.adapters; |
|||
|
|||
adapters.add = function (adapterName, params, fn) { |
|||
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation.</summary>
|
|||
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
|||
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
|||
/// <param name="params" type="Array" optional="true">[Optional] An array of parameter names (strings) that will
|
|||
/// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and
|
|||
/// mmmm is the parameter name).</param>
|
|||
/// <param name="fn" type="Function">The function to call, which adapts the values from the HTML
|
|||
/// attributes into jQuery Validate rules and/or messages.</param>
|
|||
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
|||
if (!fn) { // Called with no params, just a function
|
|||
fn = params; |
|||
params = []; |
|||
} |
|||
this.push({ name: adapterName, params: params, adapt: fn }); |
|||
return this; |
|||
}; |
|||
|
|||
adapters.addBool = function (adapterName, ruleName) { |
|||
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
|||
/// the jQuery Validate validation rule has no parameter values.</summary>
|
|||
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
|||
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
|||
/// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
|
|||
/// of adapterName will be used instead.</param>
|
|||
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
|||
return this.add(adapterName, function (options) { |
|||
setValidationValues(options, ruleName || adapterName, true); |
|||
}); |
|||
}; |
|||
|
|||
adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { |
|||
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
|||
/// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and
|
|||
/// one for min-and-max). The HTML parameters are expected to be named -min and -max.</summary>
|
|||
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
|||
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
|
|||
/// <param name="minRuleName" type="String">The name of the jQuery Validate rule to be used when you only
|
|||
/// have a minimum value.</param>
|
|||
/// <param name="maxRuleName" type="String">The name of the jQuery Validate rule to be used when you only
|
|||
/// have a maximum value.</param>
|
|||
/// <param name="minMaxRuleName" type="String">The name of the jQuery Validate rule to be used when you
|
|||
/// have both a minimum and maximum value.</param>
|
|||
/// <param name="minAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
|
|||
/// contains the minimum value. The default is "min".</param>
|
|||
/// <param name="maxAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
|
|||
/// contains the maximum value. The default is "max".</param>
|
|||
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
|||
return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { |
|||
var min = options.params.min, |
|||
max = options.params.max; |
|||
|
|||
if (min && max) { |
|||
setValidationValues(options, minMaxRuleName, [min, max]); |
|||
} |
|||
else if (min) { |
|||
setValidationValues(options, minRuleName, min); |
|||
} |
|||
else if (max) { |
|||
setValidationValues(options, maxRuleName, max); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
adapters.addSingleVal = function (adapterName, attribute, ruleName) { |
|||
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
|
|||
/// the jQuery Validate validation rule has a single value.</summary>
|
|||
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
|
|||
/// in the data-val-nnnn HTML attribute(where nnnn is the adapter name).</param>
|
|||
/// <param name="attribute" type="String">[Optional] The name of the HTML attribute that contains the value.
|
|||
/// The default is "val".</param>
|
|||
/// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
|
|||
/// of adapterName will be used instead.</param>
|
|||
/// <returns type="jQuery.validator.unobtrusive.adapters" />
|
|||
return this.add(adapterName, [attribute || "val"], function (options) { |
|||
setValidationValues(options, ruleName || adapterName, options.params[attribute]); |
|||
}); |
|||
}; |
|||
|
|||
$jQval.addMethod("__dummy__", function (value, element, params) { |
|||
return true; |
|||
}); |
|||
|
|||
$jQval.addMethod("regex", function (value, element, params) { |
|||
var match; |
|||
if (this.optional(element)) { |
|||
return true; |
|||
} |
|||
|
|||
match = new RegExp(params).exec(value); |
|||
return (match && (match.index === 0) && (match[0].length === value.length)); |
|||
}); |
|||
|
|||
$jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { |
|||
var match; |
|||
if (nonalphamin) { |
|||
match = value.match(/\W/g); |
|||
match = match && match.length >= nonalphamin; |
|||
} |
|||
return match; |
|||
}); |
|||
|
|||
if ($jQval.methods.extension) { |
|||
adapters.addSingleVal("accept", "mimtype"); |
|||
adapters.addSingleVal("extension", "extension"); |
|||
} else { |
|||
// for backward compatibility, when the 'extension' validation method does not exist, such as with versions
|
|||
// of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for
|
|||
// validating the extension, and ignore mime-type validations as they are not supported.
|
|||
adapters.addSingleVal("extension", "extension", "accept"); |
|||
} |
|||
|
|||
adapters.addSingleVal("regex", "pattern"); |
|||
adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); |
|||
adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); |
|||
adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength"); |
|||
adapters.add("equalto", ["other"], function (options) { |
|||
var prefix = getModelPrefix(options.element.name), |
|||
other = options.params.other, |
|||
fullOtherName = appendModelPrefix(other, prefix), |
|||
element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; |
|||
|
|||
setValidationValues(options, "equalTo", element); |
|||
}); |
|||
adapters.add("required", function (options) { |
|||
// jQuery Validate equates "required" with "mandatory" for checkbox elements
|
|||
if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { |
|||
setValidationValues(options, "required", true); |
|||
} |
|||
}); |
|||
adapters.add("remote", ["url", "type", "additionalfields"], function (options) { |
|||
var value = { |
|||
url: options.params.url, |
|||
type: options.params.type || "GET", |
|||
data: {} |
|||
}, |
|||
prefix = getModelPrefix(options.element.name); |
|||
|
|||
$.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { |
|||
var paramName = appendModelPrefix(fieldName, prefix); |
|||
value.data[paramName] = function () { |
|||
var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']"); |
|||
// For checkboxes and radio buttons, only pick up values from checked fields.
|
|||
if (field.is(":checkbox")) { |
|||
return field.filter(":checked").val() || field.filter(":hidden").val() || ''; |
|||
} |
|||
else if (field.is(":radio")) { |
|||
return field.filter(":checked").val() || ''; |
|||
} |
|||
return field.val(); |
|||
}; |
|||
}); |
|||
|
|||
setValidationValues(options, "remote", value); |
|||
}); |
|||
adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { |
|||
if (options.params.min) { |
|||
setValidationValues(options, "minlength", options.params.min); |
|||
} |
|||
if (options.params.nonalphamin) { |
|||
setValidationValues(options, "nonalphamin", options.params.nonalphamin); |
|||
} |
|||
if (options.params.regex) { |
|||
setValidationValues(options, "regex", options.params.regex); |
|||
} |
|||
}); |
|||
adapters.add("fileextensions", ["extensions"], function (options) { |
|||
setValidationValues(options, "extension", options.params.extensions); |
|||
}); |
|||
|
|||
$(function () { |
|||
$jQval.unobtrusive.parse(document); |
|||
}); |
|||
|
|||
return $jQval.unobtrusive; |
|||
})); |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
@ -0,0 +1,163 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using System.Web.Mvc; |
|||
using Autofac; |
|||
using Autofac.Extensions.DependencyInjection; |
|||
using Autofac.Integration.Mvc; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Owin; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.Sandbox.AspNet.Server.Models; |
|||
using OpenIddict.Server.Owin; |
|||
using OpenIddict.Validation.Owin; |
|||
using Owin; |
|||
using static OpenIddict.Abstractions.OpenIddictConstants; |
|||
|
|||
[assembly: OwinStartup(typeof(OpenIddict.Sandbox.AspNet.Server.Startup))] |
|||
namespace OpenIddict.Sandbox.AspNet.Server |
|||
{ |
|||
public partial class Startup |
|||
{ |
|||
public void Configuration(IAppBuilder app) |
|||
{ |
|||
ConfigureAuth(app); |
|||
|
|||
var container = CreateContainer(); |
|||
|
|||
// Register the Autofac scope injector middleware.
|
|||
app.UseAutofacLifetimeScopeInjector(container); |
|||
|
|||
// Register the two OpenIddict server/validation middleware.
|
|||
app.UseMiddlewareFromContainer<OpenIddictServerOwinMiddleware>(); |
|||
app.UseMiddlewareFromContainer<OpenIddictValidationOwinMiddleware>(); |
|||
|
|||
// Configure ASP.NET MVC 5.2 to use Autofac when activating controller instances.
|
|||
DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); |
|||
|
|||
// Seed the database with the sample client using the OpenIddict application manager.
|
|||
// Note: in a real world application, this step should be part of a setup script.
|
|||
Task.Run(async delegate |
|||
{ |
|||
using var scope = container.BeginLifetimeScope(); |
|||
|
|||
var context = scope.Resolve<ApplicationDbContext>(); |
|||
context.Database.CreateIfNotExists(); |
|||
|
|||
var manager = scope.Resolve<IOpenIddictApplicationManager>(); |
|||
|
|||
if (await manager.FindByClientIdAsync("mvc") is null) |
|||
{ |
|||
await manager.CreateAsync(new OpenIddictApplicationDescriptor |
|||
{ |
|||
ClientId = "mvc", |
|||
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654", |
|||
ConsentType = ConsentTypes.Explicit, |
|||
DisplayName = "MVC client application", |
|||
RedirectUris = |
|||
{ |
|||
new Uri("https://localhost:44378/signin-oidc") |
|||
}, |
|||
Permissions = |
|||
{ |
|||
Permissions.Endpoints.Authorization, |
|||
Permissions.Endpoints.Logout, |
|||
Permissions.Endpoints.Token, |
|||
Permissions.GrantTypes.AuthorizationCode, |
|||
Permissions.GrantTypes.RefreshToken, |
|||
Permissions.ResponseTypes.Code, |
|||
Permissions.Scopes.Email, |
|||
Permissions.Scopes.Profile, |
|||
Permissions.Scopes.Roles, |
|||
Permissions.Prefixes.Scope + "demo_api" |
|||
}, |
|||
PostLogoutRedirectUris = |
|||
{ |
|||
new Uri("https://localhost:44378/Account/SignOutCallback") |
|||
}, |
|||
Requirements = |
|||
{ |
|||
Requirements.Features.ProofKeyForCodeExchange |
|||
} |
|||
}); |
|||
} |
|||
}).GetAwaiter().GetResult(); |
|||
} |
|||
|
|||
private static IContainer CreateContainer() |
|||
{ |
|||
var services = new ServiceCollection(); |
|||
|
|||
services.AddOpenIddict() |
|||
|
|||
// Register the OpenIddict core components.
|
|||
.AddCore(options => |
|||
{ |
|||
// Configure OpenIddict to use the Entity Framework 6.x stores and models.
|
|||
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
|
|||
options.UseEntityFramework() |
|||
.UseDbContext<ApplicationDbContext>(); |
|||
|
|||
// Developers who prefer using MongoDB can remove the previous lines
|
|||
// and configure OpenIddict to use the specified MongoDB database:
|
|||
// options.UseMongoDb()
|
|||
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));
|
|||
}) |
|||
|
|||
// Register the OpenIddict server components.
|
|||
.AddServer(options => |
|||
{ |
|||
// Enable the authorization, device, introspection,
|
|||
// logout, token, userinfo and verification endpoints.
|
|||
options.SetAuthorizationEndpointUris("/connect/authorize") |
|||
.SetDeviceEndpointUris("/connect/device") |
|||
.SetIntrospectionEndpointUris("/connect/introspect") |
|||
.SetLogoutEndpointUris("/connect/logout") |
|||
.SetTokenEndpointUris("/connect/token") |
|||
.SetUserinfoEndpointUris("/connect/userinfo") |
|||
.SetVerificationEndpointUris("/connect/verify"); |
|||
|
|||
// Note: this sample uses the code, device code, password and refresh token flows, but you
|
|||
// can enable the other flows if you need to support implicit or client credentials.
|
|||
options.AllowAuthorizationCodeFlow() |
|||
.AllowDeviceCodeFlow() |
|||
.AllowPasswordFlow() |
|||
.AllowRefreshTokenFlow(); |
|||
|
|||
// Mark the "email", "profile", "roles" and "demo_api" scopes as supported scopes.
|
|||
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles, "demo_api"); |
|||
|
|||
// Register the signing and encryption credentials.
|
|||
options.AddDevelopmentEncryptionCertificate() |
|||
.AddDevelopmentSigningCertificate(); |
|||
|
|||
// Force client applications to use Proof Key for Code Exchange (PKCE).
|
|||
options.RequireProofKeyForCodeExchange(); |
|||
|
|||
// Register the OWIN host and configure the OWIN-specific options.
|
|||
options.UseOwin() |
|||
.EnableAuthorizationEndpointPassthrough() |
|||
.EnableLogoutEndpointPassthrough() |
|||
.EnableTokenEndpointPassthrough(); |
|||
}) |
|||
|
|||
// Register the OpenIddict validation components.
|
|||
.AddValidation(options => |
|||
{ |
|||
// Import the configuration from the local OpenIddict server instance.
|
|||
options.UseLocalServer(); |
|||
|
|||
// Register the OWIN host.
|
|||
options.UseOwin(); |
|||
}); |
|||
|
|||
// Create a new Autofac container and import the OpenIddict services.
|
|||
var builder = new ContainerBuilder(); |
|||
builder.Populate(services); |
|||
|
|||
// Register the MVC controllers.
|
|||
builder.RegisterControllers(typeof(Startup).Assembly); |
|||
|
|||
return builder.Build(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Collections.Generic; |
|||
using System.Web.Mvc; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization |
|||
{ |
|||
[Bind(Exclude = nameof(Parameters))] |
|||
public class LogoutViewModel |
|||
{ |
|||
public IEnumerable<KeyValuePair<string, string>> Parameters { get; internal set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Web.Mvc; |
|||
|
|||
namespace OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization |
|||
{ |
|||
[Bind(Exclude = nameof(Parameters))] |
|||
public class AuthorizeViewModel |
|||
{ |
|||
[Display(Name = "Application")] |
|||
public string ApplicationName { get; set; } |
|||
|
|||
[Display(Name = "Scope")] |
|||
public string Scope { get; set; } |
|||
|
|||
public IEnumerable<KeyValuePair<string, string>> Parameters { get; internal set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
@{ |
|||
ViewBag.Title = "Confirmation de l'e-mail"; |
|||
} |
|||
|
|||
<h2>@ViewBag.Title.</h2> |
|||
<div> |
|||
<p> |
|||
Merci d'avoir confirmé votre e-mail. Veuillez @Html.ActionLink("Cliquer ici pour vous connecter", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" }) |
|||
</p> |
|||
</div> |
|||
@ -0,0 +1,36 @@ |
|||
@model OpenIddict.Sandbox.AspNet.Server.Models.ExternalLoginConfirmationViewModel |
|||
@{ |
|||
ViewBag.Title = "S'inscrire"; |
|||
} |
|||
<h2>@ViewBag.Title.</h2> |
|||
<h3>Associer votre compte @ViewBag.LoginProvider.</h3> |
|||
|
|||
@using (Html.BeginForm("ExternalLoginConfirmation", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) |
|||
{ |
|||
@Html.AntiForgeryToken() |
|||
|
|||
<h4>Formulaire d'association</h4> |
|||
<hr /> |
|||
@Html.ValidationSummary(true, "", new { @class = "text-danger" }) |
|||
<p class="text-info"> |
|||
Vous avez été authentifié avec succès avec <strong>@ViewBag.LoginProvider</strong>. |
|||
Veuillez entrer ci-dessous un nom d'utilisateur pour ce site et cliquer sur le bouton S'inscrire pour valider la |
|||
connexion. |
|||
</p> |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) |
|||
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<div class="col-md-offset-2 col-md-10"> |
|||
<input type="submit" class="btn btn-default" value="S'inscrire" /> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
@section Scripts { |
|||
@Scripts.Render("~/bundles/jqueryval") |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
@{ |
|||
ViewBag.Title = "Échec de la connexion"; |
|||
} |
|||
|
|||
<hgroup> |
|||
<h2>@ViewBag.Title.</h2> |
|||
<h3 class="text-danger">Échec de la connexion auprès du service.</h3> |
|||
</hgroup> |
|||
@ -0,0 +1,29 @@ |
|||
@model OpenIddict.Sandbox.AspNet.Server.Models.ForgotPasswordViewModel |
|||
@{ |
|||
ViewBag.Title = "Vous avez oublié votre mot de passe ?"; |
|||
} |
|||
|
|||
<h2>@ViewBag.Title.</h2> |
|||
|
|||
@using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) |
|||
{ |
|||
@Html.AntiForgeryToken() |
|||
<h4>Entrez une adresse de messagerie.</h4> |
|||
<hr /> |
|||
@Html.ValidationSummary("", new { @class = "text-danger" }) |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<div class="col-md-offset-2 col-md-10"> |
|||
<input type="submit" class="btn btn-default" value="Lien e-mail" /> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
@section Scripts { |
|||
@Scripts.Render("~/bundles/jqueryval") |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
@{ |
|||
ViewBag.Title = "Confirmation du mot de passe oublié"; |
|||
} |
|||
|
|||
<hgroup class="title"> |
|||
<h1>@ViewBag.Title.</h1> |
|||
</hgroup> |
|||
<div> |
|||
<p> |
|||
Vérifiez votre adresse de messagerie pour réinitialiser votre mot de passe. |
|||
</p> |
|||
</div> |
|||
|
|||
@ -0,0 +1,63 @@ |
|||
@using OpenIddict.Sandbox.AspNet.Server.Models |
|||
@model LoginViewModel |
|||
@{ |
|||
ViewBag.Title = "Se connecter"; |
|||
} |
|||
|
|||
<h2>@ViewBag.Title.</h2> |
|||
<div class="row"> |
|||
<div class="col-md-8"> |
|||
<section id="loginForm"> |
|||
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) |
|||
{ |
|||
@Html.AntiForgeryToken() |
|||
<h4>Connectez-vous à l'aide d'un compte local.</h4> |
|||
<hr /> |
|||
@Html.ValidationSummary(true, "", new { @class = "text-danger" }) |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) |
|||
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.PasswordFor(m => m.Password, new { @class = "form-control" }) |
|||
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<div class="col-md-offset-2 col-md-10"> |
|||
<div class="checkbox"> |
|||
@Html.CheckBoxFor(m => m.RememberMe) |
|||
@Html.LabelFor(m => m.RememberMe) |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<div class="col-md-offset-2 col-md-10"> |
|||
<input type="submit" value="Se connecter" class="btn btn-default" /> |
|||
</div> |
|||
</div> |
|||
<p> |
|||
@Html.ActionLink("Inscrire comme nouvel utilisateur", "Register") |
|||
</p> |
|||
@* Activez ceci une fois que vous avez activé la confirmation du compte pour la fonctionnalité de réinitialisation du mot de passe |
|||
<p> |
|||
@Html.ActionLink("Vous avez oublié votre mot de passe ?", "ForgotPassword") |
|||
</p>*@ |
|||
} |
|||
</section> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
<section id="socialLoginForm"> |
|||
@Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl }) |
|||
</section> |
|||
</div> |
|||
</div> |
|||
|
|||
@section Scripts { |
|||
@Scripts.Render("~/bundles/jqueryval") |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
@model OpenIddict.Sandbox.AspNet.Server.Models.RegisterViewModel |
|||
@{ |
|||
ViewBag.Title = "Register"; |
|||
} |
|||
|
|||
<h2>@ViewBag.Title.</h2> |
|||
|
|||
@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) |
|||
{ |
|||
@Html.AntiForgeryToken() |
|||
<h4>Créez un nouveau compte.</h4> |
|||
<hr /> |
|||
@Html.ValidationSummary("", new { @class = "text-danger" }) |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.PasswordFor(m => m.Password, new { @class = "form-control" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<div class="col-md-offset-2 col-md-10"> |
|||
<input type="submit" class="btn btn-default" value="S'inscrire" /> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
@section Scripts { |
|||
@Scripts.Render("~/bundles/jqueryval") |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
@model OpenIddict.Sandbox.AspNet.Server.Models.ResetPasswordViewModel |
|||
@{ |
|||
ViewBag.Title = "Réinitialiser le mot de passe"; |
|||
} |
|||
|
|||
<h2>@ViewBag.Title.</h2> |
|||
|
|||
@using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) |
|||
{ |
|||
@Html.AntiForgeryToken() |
|||
<h4>Réinitialisez votre mot de passe.</h4> |
|||
<hr /> |
|||
@Html.ValidationSummary("", new { @class = "text-danger" }) |
|||
@Html.HiddenFor(model => model.Code) |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.PasswordFor(m => m.Password, new { @class = "form-control" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<div class="col-md-offset-2 col-md-10"> |
|||
<input type="submit" class="btn btn-default" value="Réinitialiser" /> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
@section Scripts { |
|||
@Scripts.Render("~/bundles/jqueryval") |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
@{ |
|||
ViewBag.Title = "Réinitialiser la confirmation du mot de passe"; |
|||
} |
|||
|
|||
<hgroup class="title"> |
|||
<h1>@ViewBag.Title.</h1> |
|||
</hgroup> |
|||
<div> |
|||
<p> |
|||
Votre mot de passe a été réinitialisé. Veuillez @Html.ActionLink("cliquer ici pour vous connecter", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" }) |
|||
</p> |
|||
</div> |
|||
@ -0,0 +1,24 @@ |
|||
@model OpenIddict.Sandbox.AspNet.Server.Models.SendCodeViewModel |
|||
@{ |
|||
ViewBag.Title = "Envoyer"; |
|||
} |
|||
|
|||
<h2>@ViewBag.Title.</h2> |
|||
|
|||
@using (Html.BeginForm("SendCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { |
|||
@Html.AntiForgeryToken() |
|||
@Html.Hidden("rememberMe", @Model.RememberMe) |
|||
<h4>Envoyer le code de vérification</h4> |
|||
<hr /> |
|||
<div class="row"> |
|||
<div class="col-md-8"> |
|||
Sélectionner le fournisseur d'authentification à 2 facteurs : |
|||
@Html.DropDownListFor(model => model.SelectedProvider, Model.Providers) |
|||
<input type="submit" value="Envoyer" class="btn btn-default" /> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
@section Scripts { |
|||
@Scripts.Render("~/bundles/jqueryval") |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
@model OpenIddict.Sandbox.AspNet.Server.Models.VerifyCodeViewModel |
|||
@{ |
|||
ViewBag.Title = "Vérifier"; |
|||
} |
|||
|
|||
<h2>@ViewBag.Title.</h2> |
|||
|
|||
@using (Html.BeginForm("VerifyCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { |
|||
@Html.AntiForgeryToken() |
|||
@Html.Hidden("provider", @Model.Provider) |
|||
@Html.Hidden("rememberMe", @Model.RememberMe) |
|||
<h4>Entrer le code de vérification</h4> |
|||
<hr /> |
|||
@Html.ValidationSummary("", new { @class = "text-danger" }) |
|||
<div class="form-group"> |
|||
@Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" }) |
|||
<div class="col-md-10"> |
|||
@Html.TextBoxFor(m => m.Code, new { @class = "form-control" }) |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<div class="col-md-offset-2 col-md-10"> |
|||
<div class="checkbox"> |
|||
@Html.CheckBoxFor(m => m.RememberBrowser) |
|||
@Html.LabelFor(m => m.RememberBrowser) |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<div class="col-md-offset-2 col-md-10"> |
|||
<input type="submit" class="btn btn-default" value="Envoyer" /> |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
@section Scripts { |
|||
@Scripts.Render("~/bundles/jqueryval") |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
@model OpenIddict.Sandbox.AspNet.Server.Models.ExternalLoginListViewModel |
|||
@using Microsoft.Owin.Security |
|||
|
|||
<h4>Connectez-vous à l'aide d'un autre service.</h4> |
|||
<hr /> |
|||
@{ |
|||
var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes(); |
|||
if (loginProviders.Count() == 0) { |
|||
<div> |
|||
<p> |
|||
Aucun service d'authentification externe n'est configuré. Consultez <a href="https://go.microsoft.com/fwlink/?LinkId=403804">cet article</a> |
|||
pour plus d'informations sur la configuration de cette application ASP.NET et la prise en charge des connexions via des services externes. |
|||
</p> |
|||
</div> |
|||
} |
|||
else { |
|||
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl })) { |
|||
@Html.AntiForgeryToken() |
|||
<div id="socialLoginList"> |
|||
<p> |
|||
@foreach (AuthenticationDescription p in loginProviders) { |
|||
<button type="submit" class="btn btn-default" id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType" title="Connexion avec votre compte @p.Caption">@p.AuthenticationType</button> |
|||
} |
|||
</p> |
|||
</div> |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
@using OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization |
|||
@model AuthorizeViewModel |
|||
|
|||
<div class="jumbotron"> |
|||
<h1>Authorization</h1> |
|||
|
|||
<p class="lead text-left">Do you want to grant <strong>@Model.ApplicationName</strong> access to your data? (scopes requested: @Model.Scope)</p> |
|||
|
|||
@using (Html.BeginForm()) |
|||
{ |
|||
@Html.AntiForgeryToken() |
|||
|
|||
@* Flow the request parameters so they can be received by the Accept/Reject actions: *@ |
|||
foreach (var parameter in Model.Parameters) |
|||
{ |
|||
<input type="hidden" name="@parameter.Key" value="@parameter.Value" /> |
|||
} |
|||
|
|||
<input class="btn btn-lg btn-success" name="submit.Accept" type="submit" value="Yes" /> |
|||
<input class="btn btn-lg btn-danger" name="submit.Deny" type="submit" value="No" /> |
|||
} |
|||
</div> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue