Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHao Kung <haokung+github@gmail.com>2022-11-08 01:09:27 +0300
committerHao Kung <haokung+github@gmail.com>2022-11-08 01:09:27 +0300
commitc6b5aabbcfa18d544db19edb928231804a2404b6 (patch)
treee0c1ab85448931958bc04607117d79cd1f233c36
parent1f923d46a06991662ba0271d6e5d23baaaa1e6a9 (diff)
Add schema supporthaok/idjwt
-rw-r--r--.editorconfig1
-rw-r--r--AspNetCore.sln22
-rw-r--r--src/Identity/Bearer/src/IdentityJwtHandler.cs (renamed from src/Identity/Core/src/NewStuff/IdentityJwtHandler.cs)24
-rw-r--r--src/Identity/Bearer/src/IdentityJwtOptions.cs (renamed from src/Identity/Core/src/NewStuff/IdentityJwtOptions.cs)0
-rw-r--r--src/Identity/Bearer/src/Microsoft.AspNetCore.Identity.Bearer.csproj22
-rw-r--r--src/Identity/Bearer/src/PublicAPI.Shipped.txt1
-rw-r--r--src/Identity/Bearer/src/PublicAPI.Unshipped.txt14
-rw-r--r--src/Identity/Bearer/src/TokenManager.cs (renamed from src/Identity/Extensions.Core/src/NewStuff/TokenManager.cs)8
-rw-r--r--src/Identity/Core/src/IdentityServiceCollectionExtensions.cs8
-rw-r--r--src/Identity/Core/src/Microsoft.AspNetCore.Identity.csproj1
-rw-r--r--src/Identity/Core/src/PublicAPI.Unshipped.txt1
-rw-r--r--src/Identity/EntityFrameworkCore/src/IdentityDbContext.cs22
-rw-r--r--src/Identity/EntityFrameworkCore/src/IdentityUserContext.cs126
-rw-r--r--src/Identity/EntityFrameworkCore/src/PublicAPI.Unshipped.txt5
-rw-r--r--src/Identity/EntityFrameworkCore/test/EF.InMemory.Test/SchemaVersionTest.cs0
-rw-r--r--src/Identity/EntityFrameworkCore/test/EF.Test/VersionOneSchemaTest.cs86
-rw-r--r--src/Identity/EntityFrameworkCore/test/EF.Test/VersionTestDbContext.cs30
-rw-r--r--src/Identity/EntityFrameworkCore/test/EF.Test/VersionTwoSchemaTest.cs74
-rw-r--r--src/Identity/Extensions.Core/src/IdentityVersions.cs24
-rw-r--r--src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt7
-rw-r--r--src/Identity/Extensions.Core/src/StoreOptions.cs8
-rw-r--r--src/Identity/Identity.slnf1
22 files changed, 459 insertions, 26 deletions
diff --git a/.editorconfig b/.editorconfig
index af87d3b6e2..9627142199 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -20,7 +20,6 @@ trim_trailing_whitespace = true
insert_final_newline = true
[*.cs]
-dotnet_public_api_analyzer.require_api_files = false
indent_size = 4
dotnet_sort_system_directives_first = true
diff --git a/AspNetCore.sln b/AspNetCore.sln
index bfdd347a7b..f00fe38f2f 100644
--- a/AspNetCore.sln
+++ b/AspNetCore.sln
@@ -1752,6 +1752,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebAppSample", "src\Framewo
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F43CC5EA-6032-4A11-A9B2-6D48CB5EB082}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bearer", "Bearer", "{BAC546F7-4E56-4BA6-92A0-518EDE272AA5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Bearer", "src\Identity\Bearer\src\Microsoft.AspNetCore.Identity.Bearer.csproj", "{7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -10513,6 +10517,22 @@ Global
{A8E2AB77-8F57-47C2-A961-2F316793CAFF}.Release|x64.Build.0 = Release|Any CPU
{A8E2AB77-8F57-47C2-A961-2F316793CAFF}.Release|x86.ActiveCfg = Release|Any CPU
{A8E2AB77-8F57-47C2-A961-2F316793CAFF}.Release|x86.Build.0 = Release|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Debug|arm64.Build.0 = Debug|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Debug|x64.Build.0 = Debug|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Debug|x86.Build.0 = Debug|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Release|arm64.ActiveCfg = Release|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Release|arm64.Build.0 = Release|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Release|x64.ActiveCfg = Release|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Release|x64.Build.0 = Release|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Release|x86.ActiveCfg = Release|Any CPU
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -11378,6 +11398,8 @@ Global
{890B5210-48EF-488F-93A2-F13BCB07C780} = {4DA84F2B-1948-439B-85AB-E99E31331A9C}
{A8E2AB77-8F57-47C2-A961-2F316793CAFF} = {890B5210-48EF-488F-93A2-F13BCB07C780}
{F43CC5EA-6032-4A11-A9B2-6D48CB5EB082} = {4DA84F2B-1948-439B-85AB-E99E31331A9C}
+ {BAC546F7-4E56-4BA6-92A0-518EDE272AA5} = {9F21A235-436E-4020-A076-1DF4F89D0CA0}
+ {7E1734E7-34F0-4CF9-BD29-4878D4FF8EFB} = {BAC546F7-4E56-4BA6-92A0-518EDE272AA5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
diff --git a/src/Identity/Core/src/NewStuff/IdentityJwtHandler.cs b/src/Identity/Bearer/src/IdentityJwtHandler.cs
index 648c5f4036..4caea7b691 100644
--- a/src/Identity/Core/src/NewStuff/IdentityJwtHandler.cs
+++ b/src/Identity/Bearer/src/IdentityJwtHandler.cs
@@ -8,16 +8,28 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
-using Microsoft.Extensions.Configuration.UserSecrets;
-using System.Text.Json.Nodes;
-using System.Text.Json;
+using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Identity;
/// <summary>
-///
+/// Contains extension methods to <see cref="IServiceCollection"/> for configuring identity services.
/// </summary>
-public class IdentityJwtHandler : AuthenticationHandler<IdentityJwtOptions>
+public static class IdentityJwtServiceCollectionExtensions
+{
+ public static IdentityBuilder AddDefaultIdentityJwt<TUser>(this IServiceCollection services,
+ Action<IdentityOptions> setupAction)
+ where TUser : class
+ {
+ services.AddAuthentication(IdentityConstants.JwtScheme).AddScheme<IdentityJwtOptions, IdentityJwtHandler>(IdentityConstants.JwtScheme, configureOptions: null);
+ return services.AddIdentityCore<TUser>(setupAction);
+ }
+}
+
+ /// <summary>
+ ///
+ /// </summary>
+ public class IdentityJwtHandler : AuthenticationHandler<IdentityJwtOptions>
{
private readonly JwtSecurityTokenHandler _defaultHandler = new JwtSecurityTokenHandler();
internal static AuthenticateResult ValidatorNotFound = AuthenticateResult.Fail("No SecurityTokenValidator available for token.");
@@ -97,7 +109,7 @@ public class IdentityJwtHandler : AuthenticationHandler<IdentityJwtOptions>
var newKeyMaterial = System.Security.Cryptography.RandomNumberGenerator.GetBytes(32);
- validationParameters.IssuerSigningKey = new SymmetricSecurityKey(ReadKeyAsBytes(_jwtSettings.TokenSecretKey))
+ //validationParameters.IssuerSigningKey = new SymmetricSecurityKey(ReadKeyAsBytes(_jwtSettings.TokenSecretKey))
//if (_configuration != null)
//{
diff --git a/src/Identity/Core/src/NewStuff/IdentityJwtOptions.cs b/src/Identity/Bearer/src/IdentityJwtOptions.cs
index 5b9b449e39..5b9b449e39 100644
--- a/src/Identity/Core/src/NewStuff/IdentityJwtOptions.cs
+++ b/src/Identity/Bearer/src/IdentityJwtOptions.cs
diff --git a/src/Identity/Bearer/src/Microsoft.AspNetCore.Identity.Bearer.csproj b/src/Identity/Bearer/src/Microsoft.AspNetCore.Identity.Bearer.csproj
new file mode 100644
index 0000000000..985264ae8f
--- /dev/null
+++ b/src/Identity/Bearer/src/Microsoft.AspNetCore.Identity.Bearer.csproj
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>ASP.NET Core Identity is the membership system for building ASP.NET Core web applications, including membership, login, and user data. ASP.NET Core Identity allows you to add login features to your application and makes it easy to customize data about the logged in user.</Description>
+ <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+ <IsAspNetCoreApp>false</IsAspNetCoreApp>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <PackageTags>aspnetcore;identity;membership</PackageTags>
+ <IsPackable>false</IsPackable>
+ <IsTrimmable>true</IsTrimmable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Authentication.Cookies" />
+ <Reference Include="Microsoft.Extensions.Identity.Core" />
+ <Reference Include="Microsoft.AspNetCore.Identity" />
+ <Reference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" />
+
+ <Compile Include="$(SharedSourceRoot)TrimmingAttributes.cs" LinkBase="Shared" Condition="'$(TargetFramework)' != '$(DefaultNetCoreTargetFramework)'" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Identity/Bearer/src/PublicAPI.Shipped.txt b/src/Identity/Bearer/src/PublicAPI.Shipped.txt
new file mode 100644
index 0000000000..7dc5c58110
--- /dev/null
+++ b/src/Identity/Bearer/src/PublicAPI.Shipped.txt
@@ -0,0 +1 @@
+#nullable enable
diff --git a/src/Identity/Bearer/src/PublicAPI.Unshipped.txt b/src/Identity/Bearer/src/PublicAPI.Unshipped.txt
new file mode 100644
index 0000000000..8e2a151574
--- /dev/null
+++ b/src/Identity/Bearer/src/PublicAPI.Unshipped.txt
@@ -0,0 +1,14 @@
+#nullable enable
+Microsoft.AspNetCore.Identity.IdentityJwtServiceCollectionExtensions
+Microsoft.AspNetCore.Identity.ITokenStore<TUser>
+Microsoft.AspNetCore.Identity.TokenManager<TUser>
+Microsoft.AspNetCore.Identity.TokenManager<TUser>.Dispose() -> void
+Microsoft.AspNetCore.Identity.TokenManager<TUser>.ErrorDescriber.get -> Microsoft.AspNetCore.Identity.IdentityErrorDescriber!
+Microsoft.AspNetCore.Identity.TokenManager<TUser>.ErrorDescriber.set -> void
+Microsoft.AspNetCore.Identity.TokenManager<TUser>.Store.get -> Microsoft.AspNetCore.Identity.ITokenStore<TUser!>!
+Microsoft.AspNetCore.Identity.TokenManager<TUser>.ThrowIfDisposed() -> void
+Microsoft.AspNetCore.Identity.TokenManager<TUser>.TokenManager(Microsoft.AspNetCore.Identity.ITokenStore<TUser!>! store, Microsoft.AspNetCore.Identity.IdentityErrorDescriber! errors, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.TokenManager<TUser!>!>! logger) -> void
+static Microsoft.AspNetCore.Identity.IdentityJwtServiceCollectionExtensions.AddDefaultIdentityJwt<TUser>(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<Microsoft.AspNetCore.Identity.IdentityOptions!>! setupAction) -> Microsoft.AspNetCore.Identity.IdentityBuilder!
+virtual Microsoft.AspNetCore.Identity.TokenManager<TUser>.CancellationToken.get -> System.Threading.CancellationToken
+virtual Microsoft.AspNetCore.Identity.TokenManager<TUser>.Dispose(bool disposing) -> void
+virtual Microsoft.AspNetCore.Identity.TokenManager<TUser>.Logger.get -> Microsoft.Extensions.Logging.ILogger!
diff --git a/src/Identity/Extensions.Core/src/NewStuff/TokenManager.cs b/src/Identity/Bearer/src/TokenManager.cs
index 520ce9e732..11611d7c4d 100644
--- a/src/Identity/Extensions.Core/src/NewStuff/TokenManager.cs
+++ b/src/Identity/Bearer/src/TokenManager.cs
@@ -1,14 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Security.Claims;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Identity.Core;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Identity;
diff --git a/src/Identity/Core/src/IdentityServiceCollectionExtensions.cs b/src/Identity/Core/src/IdentityServiceCollectionExtensions.cs
index 125bec2498..ee8c3d9569 100644
--- a/src/Identity/Core/src/IdentityServiceCollectionExtensions.cs
+++ b/src/Identity/Core/src/IdentityServiceCollectionExtensions.cs
@@ -14,14 +14,6 @@ namespace Microsoft.Extensions.DependencyInjection;
/// </summary>
public static class IdentityServiceCollectionExtensions
{
- public static IdentityBuilder AddDefaultIdentityJwt<TUser>(this IServiceCollection services,
- Action<IdentityOptions> setupAction)
- where TUser : class
- {
- services.AddAuthentication(IdentityConstants.JwtScheme).AddScheme<IdentityJwtOptions, IdentityJwtHandler>(IdentityConstants.JwtScheme, configureOptions: null);
- return services.AddIdentityCore<TUser>(setupAction);
- }
-
/// <summary>
/// Adds the default identity system configuration for the specified User and Role types.
/// </summary>
diff --git a/src/Identity/Core/src/Microsoft.AspNetCore.Identity.csproj b/src/Identity/Core/src/Microsoft.AspNetCore.Identity.csproj
index fcd52523bf..325b45fb7e 100644
--- a/src/Identity/Core/src/Microsoft.AspNetCore.Identity.csproj
+++ b/src/Identity/Core/src/Microsoft.AspNetCore.Identity.csproj
@@ -13,7 +13,6 @@
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Authentication.Cookies" />
<Reference Include="Microsoft.Extensions.Identity.Core" />
- <Reference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" />
<Compile Include="$(SharedSourceRoot)TrimmingAttributes.cs" LinkBase="Shared" Condition="'$(TargetFramework)' != '$(DefaultNetCoreTargetFramework)'" />
</ItemGroup>
diff --git a/src/Identity/Core/src/PublicAPI.Unshipped.txt b/src/Identity/Core/src/PublicAPI.Unshipped.txt
index 7dc5c58110..37aa4d9900 100644
--- a/src/Identity/Core/src/PublicAPI.Unshipped.txt
+++ b/src/Identity/Core/src/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
#nullable enable
+static readonly Microsoft.AspNetCore.Identity.IdentityConstants.JwtScheme -> string!
diff --git a/src/Identity/EntityFrameworkCore/src/IdentityDbContext.cs b/src/Identity/EntityFrameworkCore/src/IdentityDbContext.cs
index f4db2350d3..89870b6e19 100644
--- a/src/Identity/EntityFrameworkCore/src/IdentityDbContext.cs
+++ b/src/Identity/EntityFrameworkCore/src/IdentityDbContext.cs
@@ -119,6 +119,28 @@ public abstract class IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRol
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
+ }
+
+ /// <summary>
+ /// Configures the schema needed for the identity framework for schema version 2.0
+ /// </summary>
+ /// <param name="builder">
+ /// The builder being used to construct the model for this context.
+ /// </param>
+ protected override void OnModelCreatingVersion2(ModelBuilder builder)
+ {
+ OnModelCreatingVersion1(builder);
+ }
+
+ /// <summary>
+ /// Configures the schema needed for the identity framework for schema version 1.0
+ /// </summary>
+ /// <param name="builder">
+ /// The builder being used to construct the model for this context.
+ /// </param>
+ protected override void OnModelCreatingVersion1(ModelBuilder builder)
+ {
+ base.OnModelCreatingVersion1(builder);
builder.Entity<TUser>(b =>
{
b.HasMany<TUserRole>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
diff --git a/src/Identity/EntityFrameworkCore/src/IdentityUserContext.cs b/src/Identity/EntityFrameworkCore/src/IdentityUserContext.cs
index 07f6acacfe..a49b97cc7a 100644
--- a/src/Identity/EntityFrameworkCore/src/IdentityUserContext.cs
+++ b/src/Identity/EntityFrameworkCore/src/IdentityUserContext.cs
@@ -115,6 +115,132 @@ public abstract class IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, T
/// </param>
protected override void OnModelCreating(ModelBuilder builder)
{
+ var version = GetStoreOptions()?.SchemaVersion ?? IdentityVersions.Version1;
+ OnModelCreatingVersion(builder, version);
+ }
+
+ /// <summary>
+ /// Configures the schema needed for the identity framework for a specific schema version.
+ /// </summary>
+ /// <param name="builder">
+ /// The builder being used to construct the model for this context.
+ /// </param>
+ /// <param name="schemaVersion">The schema version.</param>
+ protected virtual void OnModelCreatingVersion(ModelBuilder builder, Version schemaVersion)
+ {
+ if (schemaVersion >= IdentityVersions.Version2)
+ {
+ OnModelCreatingVersion2(builder);
+ }
+ else
+ {
+ OnModelCreatingVersion1(builder);
+ }
+ }
+
+ /// <summary>
+ /// Configures the schema needed for the identity framework for schema version 2.0
+ /// </summary>
+ /// <param name="builder">
+ /// The builder being used to construct the model for this context.
+ /// </param>
+ protected virtual void OnModelCreatingVersion2(ModelBuilder builder)
+ {
+ // Differences from Version 1 is maxKeyLength defaults to 128
+
+ var storeOptions = GetStoreOptions();
+ var maxKeyLength = storeOptions?.MaxLengthForKeys ?? 128;
+ var encryptPersonalData = storeOptions?.ProtectPersonalData ?? false;
+ PersonalDataConverter? converter = null;
+
+ builder.Entity<TUser>(b =>
+ {
+ b.HasKey(u => u.Id);
+ b.HasIndex(u => u.NormalizedUserName).HasDatabaseName("UserNameIndex").IsUnique();
+ b.HasIndex(u => u.NormalizedEmail).HasDatabaseName("EmailIndex");
+ b.ToTable("AspNetUsers");
+ b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();
+
+ b.Property(u => u.UserName).HasMaxLength(256);
+ b.Property(u => u.NormalizedUserName).HasMaxLength(256);
+ b.Property(u => u.Email).HasMaxLength(256);
+ b.Property(u => u.NormalizedEmail).HasMaxLength(256);
+
+ if (encryptPersonalData)
+ {
+ converter = new PersonalDataConverter(this.GetService<IPersonalDataProtector>());
+ var personalDataProps = typeof(TUser).GetProperties().Where(
+ prop => Attribute.IsDefined(prop, typeof(ProtectedPersonalDataAttribute)));
+ foreach (var p in personalDataProps)
+ {
+ if (p.PropertyType != typeof(string))
+ {
+ throw new InvalidOperationException(Resources.CanOnlyProtectStrings);
+ }
+ b.Property(typeof(string), p.Name).HasConversion(converter);
+ }
+ }
+
+ b.HasMany<TUserClaim>().WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
+ b.HasMany<TUserLogin>().WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
+ b.HasMany<TUserToken>().WithOne().HasForeignKey(ut => ut.UserId).IsRequired();
+ });
+
+ builder.Entity<TUserClaim>(b =>
+ {
+ b.HasKey(uc => uc.Id);
+ b.ToTable("AspNetUserClaims");
+ });
+
+ builder.Entity<TUserLogin>(b =>
+ {
+ b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
+
+ if (maxKeyLength > 0)
+ {
+ b.Property(l => l.LoginProvider).HasMaxLength(maxKeyLength);
+ b.Property(l => l.ProviderKey).HasMaxLength(maxKeyLength);
+ }
+
+ b.ToTable("AspNetUserLogins");
+ });
+
+ builder.Entity<TUserToken>(b =>
+ {
+ b.HasKey(t => new { t.UserId, t.LoginProvider, t.Name });
+
+ if (maxKeyLength > 0)
+ {
+ b.Property(t => t.LoginProvider).HasMaxLength(maxKeyLength);
+ b.Property(t => t.Name).HasMaxLength(maxKeyLength);
+ }
+
+ if (encryptPersonalData)
+ {
+ var tokenProps = typeof(TUserToken).GetProperties().Where(
+ prop => Attribute.IsDefined(prop, typeof(ProtectedPersonalDataAttribute)));
+ foreach (var p in tokenProps)
+ {
+ if (p.PropertyType != typeof(string))
+ {
+ throw new InvalidOperationException(Resources.CanOnlyProtectStrings);
+ }
+ b.Property(typeof(string), p.Name).HasConversion(converter);
+ }
+ }
+
+ b.ToTable("AspNetUserTokens");
+ });
+ }
+
+ /// <summary>
+ /// Configures the schema needed for the identity framework for schema version 1.0
+ /// </summary>
+ /// <param name="builder">
+ /// The builder being used to construct the model for this context.
+ /// </param>
+ protected virtual void OnModelCreatingVersion1(ModelBuilder builder)
+ {
var storeOptions = GetStoreOptions();
var maxKeyLength = storeOptions?.MaxLengthForKeys ?? 0;
var encryptPersonalData = storeOptions?.ProtectPersonalData ?? false;
diff --git a/src/Identity/EntityFrameworkCore/src/PublicAPI.Unshipped.txt b/src/Identity/EntityFrameworkCore/src/PublicAPI.Unshipped.txt
index 5d7f33646a..465872d1a8 100644
--- a/src/Identity/EntityFrameworkCore/src/PublicAPI.Unshipped.txt
+++ b/src/Identity/EntityFrameworkCore/src/PublicAPI.Unshipped.txt
@@ -1,2 +1,7 @@
#nullable enable
+override Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>.OnModelCreatingVersion1(Microsoft.EntityFrameworkCore.ModelBuilder! builder) -> void
+override Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>.OnModelCreatingVersion2(Microsoft.EntityFrameworkCore.ModelBuilder! builder) -> void
+virtual Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.OnModelCreatingVersion(Microsoft.EntityFrameworkCore.ModelBuilder! builder, System.Version! schemaVersion) -> void
+virtual Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.OnModelCreatingVersion1(Microsoft.EntityFrameworkCore.ModelBuilder! builder) -> void
+virtual Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.OnModelCreatingVersion2(Microsoft.EntityFrameworkCore.ModelBuilder! builder) -> void
virtual Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore<TRole, TContext, TKey, TUserRole, TRoleClaim>.SaveChanges(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
diff --git a/src/Identity/EntityFrameworkCore/test/EF.InMemory.Test/SchemaVersionTest.cs b/src/Identity/EntityFrameworkCore/test/EF.InMemory.Test/SchemaVersionTest.cs
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/Identity/EntityFrameworkCore/test/EF.InMemory.Test/SchemaVersionTest.cs
diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/VersionOneSchemaTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/VersionOneSchemaTest.cs
new file mode 100644
index 0000000000..f8df5db971
--- /dev/null
+++ b/src/Identity/EntityFrameworkCore/test/EF.Test/VersionOneSchemaTest.cs
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Testing;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test;
+
+public class VersionOneSchemaTest : IClassFixture<ScratchDatabaseFixture>
+{
+ private readonly ApplicationBuilder _builder;
+
+ public VersionOneSchemaTest(ScratchDatabaseFixture fixture)
+ {
+ var services = new ServiceCollection();
+
+ services
+ .AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
+ .AddDbContext<VersionTestDbContext>(o =>
+ o.UseSqlite(fixture.Connection)
+ .ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning)))
+ .AddIdentity<IdentityUser, IdentityRole>(o =>
+ {
+ o.Stores.MaxLengthForKeys = 128;
+ })
+ .AddEntityFrameworkStores<VersionTestDbContext>();
+
+ services.AddLogging();
+
+ _builder = new ApplicationBuilder(services.BuildServiceProvider());
+
+ using (var scope = _builder.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
+ {
+ var db = scope.ServiceProvider.GetRequiredService<VersionTestDbContext>();
+ db.Database.EnsureCreated();
+ Assert.True(db.OnModelCreatingVersion1Called);
+ }
+ }
+
+ [ConditionalFact]
+ public void EnsureDefaultSchema()
+ {
+ using (var scope = _builder.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
+ {
+ var db = scope.ServiceProvider.GetRequiredService<VersionTestDbContext>();
+
+ VerifyDefaultSchema(db);
+ }
+ }
+
+ private static void VerifyDefaultSchema(VersionTestDbContext dbContext)
+ {
+ var sqlConn = (SqliteConnection)dbContext.Database.GetDbConnection();
+
+ try
+ {
+ sqlConn.Open();
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp",
+ "EmailConfirmed", "PhoneNumber", "PhoneNumberConfirmed", "TwoFactorEnabled", "LockoutEnabled",
+ "LockoutEnd", "AccessFailedCount", "ConcurrencyStamp", "NormalizedUserName", "NormalizedEmail"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetRoles", "Id", "Name", "NormalizedName", "ConcurrencyStamp"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserRoles", "UserId", "RoleId"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserClaims", "Id", "UserId", "ClaimType", "ClaimValue"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider", "ProviderDisplayName"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserTokens", "UserId", "LoginProvider", "Name", "Value"));
+
+ Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUsers", 256, "UserName", "Email", "NormalizedUserName", "NormalizedEmail"));
+ Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetRoles", 256, "Name", "NormalizedName"));
+ Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUserLogins", 128, "LoginProvider", "ProviderKey"));
+ Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUserTokens", 128, "LoginProvider", "Name"));
+
+ DbUtil.VerifyIndex(sqlConn, "AspNetRoles", "RoleNameIndex", isUnique: true);
+ DbUtil.VerifyIndex(sqlConn, "AspNetUsers", "UserNameIndex", isUnique: true);
+ DbUtil.VerifyIndex(sqlConn, "AspNetUsers", "EmailIndex");
+ }
+ finally
+ {
+ sqlConn.Close();
+ }
+ }
+}
diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/VersionTestDbContext.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/VersionTestDbContext.cs
new file mode 100644
index 0000000000..28e876790c
--- /dev/null
+++ b/src/Identity/EntityFrameworkCore/test/EF.Test/VersionTestDbContext.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore;
+
+namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test;
+
+// Need a different context type here since EF model is changing by having MaxLengthForKeys
+public class VersionTestDbContext : IdentityDbContext<IdentityUser, IdentityRole, string>
+{
+ public VersionTestDbContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ public bool OnModelCreatingVersion1Called = false;
+ public bool OnModelCreatingVersion2Called = false;
+
+ protected override void OnModelCreatingVersion1(ModelBuilder builder)
+ {
+ base.OnModelCreatingVersion1(builder);
+ OnModelCreatingVersion1Called = true;
+ }
+
+ protected override void OnModelCreatingVersion2(ModelBuilder builder)
+ {
+ base.OnModelCreatingVersion2(builder);
+ OnModelCreatingVersion2Called = true;
+ }
+}
diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/VersionTwoSchemaTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/VersionTwoSchemaTest.cs
new file mode 100644
index 0000000000..906bc81af0
--- /dev/null
+++ b/src/Identity/EntityFrameworkCore/test/EF.Test/VersionTwoSchemaTest.cs
@@ -0,0 +1,74 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Testing;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test;
+
+public class VersionTwoSchemaTest : IClassFixture<ScratchDatabaseFixture>
+{
+ private readonly ApplicationBuilder _builder;
+
+ public VersionTwoSchemaTest(ScratchDatabaseFixture fixture)
+ {
+ var services = new ServiceCollection();
+
+ services
+ .AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
+ .AddDbContext<VersionTestDbContext>(o =>
+ o.UseSqlite(fixture.Connection)
+ .ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning)))
+ .AddIdentity<IdentityUser, IdentityRole>(o =>
+ {
+ o.Stores.SchemaVersion = IdentityVersions.Version2;
+ })
+ .AddEntityFrameworkStores<VersionTestDbContext>();
+
+ services.AddLogging();
+
+ _builder = new ApplicationBuilder(services.BuildServiceProvider());
+
+ using (var scope = _builder.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
+ {
+ var db = scope.ServiceProvider.GetRequiredService<VersionTestDbContext>();
+ db.Database.EnsureCreated();
+ Assert.True(db.OnModelCreatingVersion2Called);
+ }
+ }
+ private static void VerifyDefaultSchema(VersionTestDbContext dbContext)
+ {
+ var sqlConn = (SqliteConnection)dbContext.Database.GetDbConnection();
+
+ try
+ {
+ sqlConn.Open();
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp",
+ "EmailConfirmed", "PhoneNumber", "PhoneNumberConfirmed", "TwoFactorEnabled", "LockoutEnabled",
+ "LockoutEnd", "AccessFailedCount", "ConcurrencyStamp", "NormalizedUserName", "NormalizedEmail"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetRoles", "Id", "Name", "NormalizedName", "ConcurrencyStamp"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserRoles", "UserId", "RoleId"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserClaims", "Id", "UserId", "ClaimType", "ClaimValue"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider", "ProviderDisplayName"));
+ Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserTokens", "UserId", "LoginProvider", "Name", "Value"));
+
+ Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUsers", 256, "UserName", "Email", "NormalizedUserName", "NormalizedEmail"));
+ Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetRoles", 256, "Name", "NormalizedName"));
+ Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUserLogins", 128, "LoginProvider", "ProviderKey"));
+ Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUserTokens", 128, "LoginProvider", "Name"));
+
+ DbUtil.VerifyIndex(sqlConn, "AspNetRoles", "RoleNameIndex", isUnique: true);
+ DbUtil.VerifyIndex(sqlConn, "AspNetUsers", "UserNameIndex", isUnique: true);
+ DbUtil.VerifyIndex(sqlConn, "AspNetUsers", "EmailIndex");
+ }
+ finally
+ {
+ sqlConn.Close();
+ }
+ }
+}
diff --git a/src/Identity/Extensions.Core/src/IdentityVersions.cs b/src/Identity/Extensions.Core/src/IdentityVersions.cs
new file mode 100644
index 0000000000..5c81cc848b
--- /dev/null
+++ b/src/Identity/Extensions.Core/src/IdentityVersions.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.AspNetCore.Identity;
+
+/// <summary>
+/// Contains various identity version constants.
+/// </summary>
+public static class IdentityVersions
+{
+ /// <summary>
+ /// Represents the initial 1.0 version of the identity schema
+ /// </summary>
+ public static readonly Version Version1 = new Version(1, 0);
+
+ /// <summary>
+ /// Represents the 2.0 version of the identity schema which adds the functionality
+ /// needed for Identity.Bearer
+ /// </summary>
+ public static readonly Version Version2 = new Version(1, 0);
+
+}
diff --git a/src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt b/src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
index 58f1cf4295..f7c9d91c5c 100644
--- a/src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
+++ b/src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
@@ -1,3 +1,6 @@
#nullable enable
-Microsoft.AspNetCore.Identity.ITokenStore<TUser>
-Microsoft.AspNetCore.Identity.TokenManager<TUser>
+Microsoft.AspNetCore.Identity.IdentityVersions
+Microsoft.AspNetCore.Identity.StoreOptions.SchemaVersion.get -> System.Version!
+Microsoft.AspNetCore.Identity.StoreOptions.SchemaVersion.set -> void
+static readonly Microsoft.AspNetCore.Identity.IdentityVersions.Version1 -> System.Version!
+static readonly Microsoft.AspNetCore.Identity.IdentityVersions.Version2 -> System.Version!
diff --git a/src/Identity/Extensions.Core/src/StoreOptions.cs b/src/Identity/Extensions.Core/src/StoreOptions.cs
index efbb0c16b0..7053853803 100644
--- a/src/Identity/Extensions.Core/src/StoreOptions.cs
+++ b/src/Identity/Extensions.Core/src/StoreOptions.cs
@@ -3,6 +3,8 @@
namespace Microsoft.AspNetCore.Identity;
+using System;
+
/// <summary>
/// Used for store specific options
/// </summary>
@@ -19,4 +21,10 @@ public class StoreOptions
/// This will be enforced by requiring the store to implement <see cref="IProtectedUserStore{TUser}"/>.
/// </summary>
public bool ProtectPersonalData { get; set; }
+
+ /// <summary>
+ /// The schema version for the store, the default is 0.0 which leaves it up to the store
+ /// to determine what version should be used.
+ /// </summary>
+ public Version SchemaVersion { get; set; } = new Version(0, 0);
}
diff --git a/src/Identity/Identity.slnf b/src/Identity/Identity.slnf
index cd63efdba1..f4c30d8796 100644
--- a/src/Identity/Identity.slnf
+++ b/src/Identity/Identity.slnf
@@ -5,6 +5,7 @@
"src\\Identity\\ApiAuthorization.IdentityServer\\samples\\ApiAuthSample\\ApiAuthSample.csproj",
"src\\Identity\\ApiAuthorization.IdentityServer\\src\\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj",
"src\\Identity\\ApiAuthorization.IdentityServer\\test\\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Tests.csproj",
+ "src\\Identity\\Bearer\\src\\Microsoft.AspNetCore.Identity.Bearer.csproj",
"src\\Identity\\Core\\src\\Microsoft.AspNetCore.Identity.csproj",
"src\\Identity\\EntityFrameworkCore\\src\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj",
"src\\Identity\\EntityFrameworkCore\\test\\EF.InMemory.Test\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test.csproj",