Update Identity server dependency for preview4 (#9104)

* Updates Identity server dependency for preview4
* Infrastructure improvements.
* Updates react versions.
* Updates migrations.
* Makes templates show up in Visual Studio
This commit is contained in:
Javier Calvarro Nelson 2019-04-08 17:06:58 +02:00 committed by GitHub
parent 74bba27f43
commit 4abf766c3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 14320 additions and 9108 deletions

View File

@ -26,7 +26,9 @@ and are generated based on the last package release.
<LatestPackageReference Include="Google.ProtoBuf" Version="$(GoogleProtoBufPackageVersion)" /> <LatestPackageReference Include="Google.ProtoBuf" Version="$(GoogleProtoBufPackageVersion)" />
<LatestPackageReference Include="IdentityServer4.AspNetIdentity" Version="$(IdentityServer4AspNetIdentityPackageVersion)" /> <LatestPackageReference Include="IdentityServer4.AspNetIdentity" Version="$(IdentityServer4AspNetIdentityPackageVersion)" />
<LatestPackageReference Include="IdentityServer4.EntityFramework" Version="$(IdentityServer4EntityFrameworkPackageVersion)" /> <LatestPackageReference Include="IdentityServer4.EntityFramework" Version="$(IdentityServer4EntityFrameworkPackageVersion)" />
<LatestPackageReference Include="IdentityServer4.EntityFramework.Storage" Version="$(IdentityServer4EntityFrameworkStoragePackageVersion)" />
<LatestPackageReference Include="IdentityServer4" Version="$(IdentityServer4PackageVersion)" /> <LatestPackageReference Include="IdentityServer4" Version="$(IdentityServer4PackageVersion)" />
<LatestPackageReference Include="IdentityServer4.Storage" Version="$(IdentityServer4StoragePackageVersion)" />
<LatestPackageReference Include="Libuv" Version="$(LibuvPackageVersion)" /> <LatestPackageReference Include="Libuv" Version="$(LibuvPackageVersion)" />
<LatestPackageReference Include="MessagePack" Version="$(MessagePackPackageVersion)" /> <LatestPackageReference Include="MessagePack" Version="$(MessagePackPackageVersion)" />
<LatestPackageReference Include="Microsoft.AspNet.WebApi.Client" Version="$(MicrosoftAspNetWebApiClientPackageVersion)" /> <LatestPackageReference Include="Microsoft.AspNet.WebApi.Client" Version="$(MicrosoftAspNetWebApiClientPackageVersion)" />

View File

@ -176,9 +176,11 @@
<GoogleProtobufPackageVersion>3.7.0</GoogleProtobufPackageVersion> <GoogleProtobufPackageVersion>3.7.0</GoogleProtobufPackageVersion>
<GrpcAspNetCoreServerPackageVersion>0.1.19-pre2</GrpcAspNetCoreServerPackageVersion> <GrpcAspNetCoreServerPackageVersion>0.1.19-pre2</GrpcAspNetCoreServerPackageVersion>
<GrpcToolsPackageVersion>1.20.0-pre1</GrpcToolsPackageVersion> <GrpcToolsPackageVersion>1.20.0-pre1</GrpcToolsPackageVersion>
<IdentityServer4AspNetIdentityPackageVersion>2.3.0</IdentityServer4AspNetIdentityPackageVersion> <IdentityServer4AspNetIdentityPackageVersion>3.0.0-preview3.4</IdentityServer4AspNetIdentityPackageVersion>
<IdentityServer4EntityFrameworkPackageVersion>2.3.0</IdentityServer4EntityFrameworkPackageVersion> <IdentityServer4EntityFrameworkPackageVersion>3.0.0-preview3.4</IdentityServer4EntityFrameworkPackageVersion>
<IdentityServer4PackageVersion>2.3.0</IdentityServer4PackageVersion> <IdentityServer4PackageVersion>3.0.0-preview3.4</IdentityServer4PackageVersion>
<IdentityServer4StoragePackageVersion>3.0.0-preview3.4</IdentityServer4StoragePackageVersion>
<IdentityServer4EntityFrameworkStoragePackageVersion>3.0.0-preview3.4</IdentityServer4EntityFrameworkStoragePackageVersion>
<MessagePackPackageVersion>1.7.3.4</MessagePackPackageVersion> <MessagePackPackageVersion>1.7.3.4</MessagePackPackageVersion>
<MoqPackageVersion>4.10.0</MoqPackageVersion> <MoqPackageVersion>4.10.0</MoqPackageVersion>
<MonoCecilPackageVersion>0.10.1</MonoCecilPackageVersion> <MonoCecilPackageVersion>0.10.1</MonoCecilPackageVersion>

View File

@ -7,7 +7,9 @@
<Compile Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer.netcoreapp3.0.cs" /> <Compile Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer.netcoreapp3.0.cs" />
<Reference Include="IdentityServer4.AspNetIdentity" /> <Reference Include="IdentityServer4.AspNetIdentity" />
<Reference Include="IdentityServer4.EntityFramework" /> <Reference Include="IdentityServer4.EntityFramework" />
<Reference Include="IdentityServer4.EntityFramework.Storage" />
<Reference Include="IdentityServer4" /> <Reference Include="IdentityServer4" />
<Reference Include="IdentityServer4.Storage" />
<Reference Include="Microsoft.AspNetCore.Authentication.JwtBearer" /> <Reference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<Reference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" /> <Reference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<Reference Include="Microsoft.AspNetCore.Identity.UI" /> <Reference Include="Microsoft.AspNetCore.Identity.UI" />

View File

@ -1,4 +1,4 @@
@page @page
@addTagHelper *, Microsoft.AspNetCore.ApiAuthorization.IdentityServer @addTagHelper *, Microsoft.AspNetCore.ApiAuthorization.IdentityServer
@model ApiAuthSample.Pages.IndexModel @model ApiAuthSample.Pages.IndexModel
@ -9,6 +9,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<base href="~/" />
<title>@ViewData["Title"]</title> <title>@ViewData["Title"]</title>
</head> </head>
<body> <body>
@ -18,7 +19,7 @@
<button id="call-api" disabled>Call API</button> <button id="call-api" disabled>Call API</button>
<div id="login-result"></div> <div id="login-result"></div>
<div id="api-result"></div> <div id="api-result"></div>
<script src="js/oidc-client.js"></script> <script src="/js/oidc-client.js"></script>
<script id="apiauth" type="text/javascript" asp-apiauth-parameters="ApiAuthSampleSPA"> <script id="apiauth" type="text/javascript" asp-apiauth-parameters="ApiAuthSampleSPA">
let $data = document.querySelector("#apiauth"); let $data = document.querySelector("#apiauth");
let configuration = {}; let configuration = {};
@ -30,4 +31,4 @@
</script> </script>
<script src="js/app.js"></script> <script src="js/app.js"></script>
</body> </body>
</html> </html>

View File

@ -67,6 +67,7 @@ namespace ApiAuthSample
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers(); endpoints.MapControllers();
endpoints.MapFallbackToPage("/Index");
}); });
} }
} }

View File

@ -51,46 +51,7 @@ namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
ConfigureGrantContext(builder, _operationalStoreOptions.Value); builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value);
} }
private void ConfigureGrantContext(ModelBuilder modelBuilder, OperationalStoreOptions storeOptions)
{
if (!string.IsNullOrWhiteSpace(storeOptions.DefaultSchema)) modelBuilder.HasDefaultSchema(storeOptions.DefaultSchema);
modelBuilder.Entity<PersistedGrant>(grant =>
{
grant.ToTable("PersistedGrants");
grant.Property(x => x.Key).HasMaxLength(200).ValueGeneratedNever();
grant.Property(x => x.Type).HasMaxLength(50).IsRequired();
grant.Property(x => x.SubjectId).HasMaxLength(200);
grant.Property(x => x.ClientId).HasMaxLength(200).IsRequired();
grant.Property(x => x.CreationTime).IsRequired();
grant.Property(x => x.Data).HasMaxLength(50000).IsRequired();
grant.HasKey(x => x.Key);
grant.HasIndex(x => new { x.SubjectId, x.ClientId, x.Type });
});
modelBuilder.Entity<DeviceFlowCodes>(codes =>
{
codes.ToTable("DeviceCodes");
codes.Property(x => x.DeviceCode).HasMaxLength(200).IsRequired();
codes.Property(x => x.UserCode).HasMaxLength(200).IsRequired();
codes.Property(x => x.SubjectId).HasMaxLength(200);
codes.Property(x => x.ClientId).HasMaxLength(200).IsRequired();
codes.Property(x => x.CreationTime).IsRequired();
codes.Property(x => x.Expiration).IsRequired();
codes.Property(x => x.Data).HasMaxLength(50000).IsRequired();
codes.HasKey(x => new { x.UserCode });
codes.HasIndex(x => x.DeviceCode).IsUnique();
codes.HasIndex(x => x.UserCode).IsUnique();
});
}
} }
} }

View File

@ -12,7 +12,9 @@
<ItemGroup> <ItemGroup>
<Reference Include="IdentityServer4.AspNetIdentity" /> <Reference Include="IdentityServer4.AspNetIdentity" />
<Reference Include="IdentityServer4.EntityFramework" /> <Reference Include="IdentityServer4.EntityFramework" />
<Reference Include="IdentityServer4.EntityFramework.Storage" />
<Reference Include="IdentityServer4" /> <Reference Include="IdentityServer4" />
<Reference Include="IdentityServer4.Storage" />
<Reference Include="Microsoft.AspNetCore.Authentication.JwtBearer" /> <Reference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<Reference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" /> <Reference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<Reference Include="Microsoft.AspNetCore.Identity.UI" /> <Reference Include="Microsoft.AspNetCore.Identity.UI" />

View File

@ -21,6 +21,11 @@
"auth": "None", "auth": "None",
"authenticationType": "NoAuth", "authenticationType": "NoAuth",
"allowUnsecured": true "allowUnsecured": true
},
{
"auth": "Individual",
"authenticationType": "IndividualAuth",
"b2cAuthenticationOptions": "Local"
} }
], ],
"ports": [ "ports": [
@ -36,4 +41,4 @@
"excludeLaunchSettings": false, "excludeLaunchSettings": false,
"minFullFrameworkVersion": "4.6.1", "minFullFrameworkVersion": "4.6.1",
"disableHttpsSymbol": "NoHttps" "disableHttpsSymbol": "NoHttps"
} }

View File

@ -50,6 +50,10 @@ interface IAuthenticationState {
providedIn: 'root' providedIn: 'root'
}) })
export class AuthorizeService { export class AuthorizeService {
// By default pop ups are disabled because they don't work properly on Edge.
// If you want to enable pop up authentication simply set this flag to false.
private popUpDisabled = true;
private userManager: UserManager; private userManager: UserManager;
private userSubject: BehaviorSubject<IUser | null> = new BehaviorSubject(null); private userSubject: BehaviorSubject<IUser | null> = new BehaviorSubject(null);
@ -90,6 +94,9 @@ export class AuthorizeService {
console.log('Silent authentication error: ', silentError); console.log('Silent authentication error: ', silentError);
try { try {
if (this.popUpDisabled) {
throw new Error('Popup disabled. Change \'authorize.service.ts:AuthorizeService.popupDisabled\' to false to enable it.');
}
user = await this.userManager.signinPopup(this.createArguments(LoginMode.PopUp)); user = await this.userManager.signinPopup(this.createArguments(LoginMode.PopUp));
this.userSubject.next(user.profile); this.userSubject.next(user.profile);
return this.success(state); return this.success(state);
@ -97,8 +104,9 @@ export class AuthorizeService {
if (popupError.message === 'Popup window closed') { if (popupError.message === 'Popup window closed') {
// The user explicitly cancelled the login action by closing an opened popup. // The user explicitly cancelled the login action by closing an opened popup.
return this.error('The user closed the window.'); return this.error('The user closed the window.');
} else if (!this.popUpDisabled) {
console.log('Popup authentication error: ', popupError);
} }
console.log('Popup authentication error: ', popupError);
// PopUps might be blocked by the user, fallback to redirect // PopUps might be blocked by the user, fallback to redirect
try { try {
@ -174,6 +182,7 @@ export class AuthorizeService {
throw new Error(`Invalid login mode '${mode}'.`); throw new Error(`Invalid login mode '${mode}'.`);
} }
} }
// We try to sign out the user in two different ways: // We try to sign out the user in two different ways:
// 1) We try to do a sign-out using a PopUp Window. This might fail if there is a // 1) We try to do a sign-out using a PopUp Window. This might fail if there is a
// Pop-Up blocker or the user has disabled PopUps. // Pop-Up blocker or the user has disabled PopUps.

View File

@ -1,4 +1,4 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using Company.WebApplication1.Data; using Company.WebApplication1.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -17,14 +17,13 @@ namespace Company.WebApplication1.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1") .HasAnnotation("ProductVersion", "3.0.0-preview4.19174.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount"); b.Property<int>("AccessFailedCount");
@ -75,7 +74,6 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{ {
b.Property<string>("UserCode") b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200); .HasMaxLength(200);
b.Property<string>("ClientId") b.Property<string>("ClientId")
@ -103,9 +101,6 @@ namespace Company.WebApplication1.Data.Migrations
b.HasIndex("DeviceCode") b.HasIndex("DeviceCode")
.IsUnique(); .IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes"); b.ToTable("DeviceCodes");
}); });
@ -142,8 +137,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken(); .IsConcurrencyToken();
@ -256,47 +250,53 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }

View File

@ -232,12 +232,6 @@ namespace Company.WebApplication1.Data.Migrations
column: "DeviceCode", column: "DeviceCode",
unique: true); unique: true);
migrationBuilder.CreateIndex(
name: "IX_DeviceCodes_UserCode",
table: "DeviceCodes",
column: "UserCode",
unique: true);
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_SubjectId_ClientId_Type", name: "IX_PersistedGrants_SubjectId_ClientId_Type",
table: "PersistedGrants", table: "PersistedGrants",

View File

@ -15,14 +15,13 @@ namespace Company.WebApplication1.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1") .HasAnnotation("ProductVersion", "3.0.0-preview4.19174.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount"); b.Property<int>("AccessFailedCount");
@ -73,7 +72,6 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{ {
b.Property<string>("UserCode") b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200); .HasMaxLength(200);
b.Property<string>("ClientId") b.Property<string>("ClientId")
@ -101,9 +99,6 @@ namespace Company.WebApplication1.Data.Migrations
b.HasIndex("DeviceCode") b.HasIndex("DeviceCode")
.IsUnique(); .IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes"); b.ToTable("DeviceCodes");
}); });
@ -140,8 +135,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken(); .IsConcurrencyToken();
@ -254,47 +248,53 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }

View File

@ -1,4 +1,4 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using Company.WebApplication1.Data; using Company.WebApplication1.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -16,12 +16,11 @@ namespace Company.WebApplication1.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1"); .HasAnnotation("ProductVersion", "3.0.0-preview4.19174.3");
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount"); b.Property<int>("AccessFailedCount");
@ -71,7 +70,6 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{ {
b.Property<string>("UserCode") b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200); .HasMaxLength(200);
b.Property<string>("ClientId") b.Property<string>("ClientId")
@ -99,9 +97,6 @@ namespace Company.WebApplication1.Data.Migrations
b.HasIndex("DeviceCode") b.HasIndex("DeviceCode")
.IsUnique(); .IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes"); b.ToTable("DeviceCodes");
}); });
@ -138,8 +133,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken(); .IsConcurrencyToken();
@ -249,47 +243,53 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }

View File

@ -229,12 +229,6 @@ namespace Company.WebApplication1.Data.Migrations
column: "DeviceCode", column: "DeviceCode",
unique: true); unique: true);
migrationBuilder.CreateIndex(
name: "IX_DeviceCodes_UserCode",
table: "DeviceCodes",
column: "UserCode",
unique: true);
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_SubjectId_ClientId_Type", name: "IX_PersistedGrants_SubjectId_ClientId_Type",
table: "PersistedGrants", table: "PersistedGrants",

View File

@ -14,12 +14,11 @@ namespace Company.WebApplication1.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1"); .HasAnnotation("ProductVersion", "3.0.0-preview4.19174.3");
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount"); b.Property<int>("AccessFailedCount");
@ -69,7 +68,6 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{ {
b.Property<string>("UserCode") b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200); .HasMaxLength(200);
b.Property<string>("ClientId") b.Property<string>("ClientId")
@ -97,9 +95,6 @@ namespace Company.WebApplication1.Data.Migrations
b.HasIndex("DeviceCode") b.HasIndex("DeviceCode")
.IsUnique(); .IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes"); b.ToTable("DeviceCodes");
}); });
@ -136,8 +131,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken(); .IsConcurrencyToken();
@ -247,47 +241,53 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }

View File

@ -21,6 +21,11 @@
"auth": "None", "auth": "None",
"authenticationType": "NoAuth", "authenticationType": "NoAuth",
"allowUnsecured": true "allowUnsecured": true
},
{
"auth": "Individual",
"authenticationType": "IndividualAuth",
"b2cAuthenticationOptions": "Local"
} }
], ],
"ports": [ "ports": [
@ -36,4 +41,4 @@
"excludeLaunchSettings": false, "excludeLaunchSettings": false,
"minFullFrameworkVersion": "4.6.1", "minFullFrameworkVersion": "4.6.1",
"disableHttpsSymbol": "NoHttps" "disableHttpsSymbol": "NoHttps"
} }

View File

@ -11,17 +11,17 @@
"react-dom": "^16.0.0", "react-dom": "^16.0.0",
"react-router-bootstrap": "^0.24.4", "react-router-bootstrap": "^0.24.4",
"react-router-dom": "^4.2.2", "react-router-dom": "^4.2.2",
"react-scripts": "^1.1.5", "react-scripts": "^2.1.8",
"reactstrap": "^6.3.0", "reactstrap": "^6.3.0",
"rimraf": "^2.6.2" "rimraf": "^2.6.2"
}, },
"devDependencies": { "devDependencies": {
"ajv": "^6.0.0", "ajv": "^6.9.1",
"babel-eslint": "^7.2.3", "babel-eslint": "^9.0.0",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"eslint": "^4.1.1", "eslint": "^5.12.0",
"eslint-config-react-app": "^2.1.0", "eslint-config-react-app": "^3.0.8",
"eslint-plugin-flowtype": "^2.50.3", "eslint-plugin-flowtype": "^3.5.1",
"eslint-plugin-import": "^2.14.0", "eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^5.1.1", "eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-react": "^7.11.1" "eslint-plugin-react": "^7.11.1"

View File

@ -7,6 +7,10 @@ export class AuthorizeService {
_user = null; _user = null;
_isAuthenticated = false; _isAuthenticated = false;
// By default pop ups are disabled because they don't work properly on Edge.
// If you want to enable pop up authentication simply set this flag to false.
_popUpDisabled = true;
async isAuthenticated() { async isAuthenticated() {
const user = await this.getUser(); const user = await this.getUser();
return !!user; return !!user;
@ -47,6 +51,10 @@ export class AuthorizeService {
console.log("Silent authentication error: ", silentError); console.log("Silent authentication error: ", silentError);
try { try {
if (this._popUpDisabled) {
throw new Error('Popup disabled. Change \'AuthorizeService.js:AuthorizeService._popupDisabled\' to false to enable it.')
}
const popUpUser = await this.userManager.signinPopup(this.createArguments(LoginMode.PopUp)); const popUpUser = await this.userManager.signinPopup(this.createArguments(LoginMode.PopUp));
this.updateState(popUpUser); this.updateState(popUpUser);
return this.success(state); return this.success(state);
@ -54,8 +62,9 @@ export class AuthorizeService {
if (popUpError.message === "Popup window closed") { if (popUpError.message === "Popup window closed") {
// The user explicitly cancelled the login action by closing an opened popup. // The user explicitly cancelled the login action by closing an opened popup.
return this.error("The user closed the window."); return this.error("The user closed the window.");
} else if (!this._popUpDisabled) {
console.log("Popup authentication error: ", popUpError);
} }
console.log("Popup authentication error: ", popUpError);
// PopUps might be blocked by the user, fallback to redirect // PopUps might be blocked by the user, fallback to redirect
try { try {

View File

@ -1,4 +1,4 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using Company.WebApplication1.Data; using Company.WebApplication1.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -17,14 +17,13 @@ namespace Company.WebApplication1.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1") .HasAnnotation("ProductVersion", "3.0.0-preview4.19174.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount"); b.Property<int>("AccessFailedCount");
@ -75,7 +74,6 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{ {
b.Property<string>("UserCode") b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200); .HasMaxLength(200);
b.Property<string>("ClientId") b.Property<string>("ClientId")
@ -103,9 +101,6 @@ namespace Company.WebApplication1.Data.Migrations
b.HasIndex("DeviceCode") b.HasIndex("DeviceCode")
.IsUnique(); .IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes"); b.ToTable("DeviceCodes");
}); });
@ -142,8 +137,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken(); .IsConcurrencyToken();
@ -256,47 +250,53 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
@ -232,12 +232,6 @@ namespace Company.WebApplication1.Data.Migrations
column: "DeviceCode", column: "DeviceCode",
unique: true); unique: true);
migrationBuilder.CreateIndex(
name: "IX_DeviceCodes_UserCode",
table: "DeviceCodes",
column: "UserCode",
unique: true);
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_SubjectId_ClientId_Type", name: "IX_PersistedGrants_SubjectId_ClientId_Type",
table: "PersistedGrants", table: "PersistedGrants",

View File

@ -15,14 +15,13 @@ namespace Company.WebApplication1.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1") .HasAnnotation("ProductVersion", "3.0.0-preview4.19174.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount"); b.Property<int>("AccessFailedCount");
@ -73,7 +72,6 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{ {
b.Property<string>("UserCode") b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200); .HasMaxLength(200);
b.Property<string>("ClientId") b.Property<string>("ClientId")
@ -101,9 +99,6 @@ namespace Company.WebApplication1.Data.Migrations
b.HasIndex("DeviceCode") b.HasIndex("DeviceCode")
.IsUnique(); .IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes"); b.ToTable("DeviceCodes");
}); });
@ -140,8 +135,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken(); .IsConcurrencyToken();
@ -254,47 +248,53 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }

View File

@ -1,4 +1,4 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using Company.WebApplication1.Data; using Company.WebApplication1.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -16,12 +16,11 @@ namespace Company.WebApplication1.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1"); .HasAnnotation("ProductVersion", "3.0.0-preview4.19174.3");
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount"); b.Property<int>("AccessFailedCount");
@ -71,7 +70,6 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{ {
b.Property<string>("UserCode") b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200); .HasMaxLength(200);
b.Property<string>("ClientId") b.Property<string>("ClientId")
@ -99,9 +97,6 @@ namespace Company.WebApplication1.Data.Migrations
b.HasIndex("DeviceCode") b.HasIndex("DeviceCode")
.IsUnique(); .IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes"); b.ToTable("DeviceCodes");
}); });
@ -138,8 +133,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken(); .IsConcurrencyToken();
@ -249,47 +243,53 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }

View File

@ -229,12 +229,6 @@ namespace Company.WebApplication1.Data.Migrations
column: "DeviceCode", column: "DeviceCode",
unique: true); unique: true);
migrationBuilder.CreateIndex(
name: "IX_DeviceCodes_UserCode",
table: "DeviceCodes",
column: "UserCode",
unique: true);
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_PersistedGrants_SubjectId_ClientId_Type", name: "IX_PersistedGrants_SubjectId_ClientId_Type",
table: "PersistedGrants", table: "PersistedGrants",

View File

@ -14,12 +14,11 @@ namespace Company.WebApplication1.Data.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.0.0-preview.19080.1"); .HasAnnotation("ProductVersion", "3.0.0-preview4.19174.3");
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b => modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount"); b.Property<int>("AccessFailedCount");
@ -69,7 +68,6 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{ {
b.Property<string>("UserCode") b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200); .HasMaxLength(200);
b.Property<string>("ClientId") b.Property<string>("ClientId")
@ -97,9 +95,6 @@ namespace Company.WebApplication1.Data.Migrations
b.HasIndex("DeviceCode") b.HasIndex("DeviceCode")
.IsUnique(); .IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes"); b.ToTable("DeviceCodes");
}); });
@ -136,8 +131,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id");
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken(); .IsConcurrencyToken();
@ -247,47 +241,53 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany() .WithMany()
.HasForeignKey("RoleId") .HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{ {
b.HasOne("Company.WebApplication1.Models.ApplicationUser") b.HasOne("Company.WebApplication1.Models.ApplicationUser", null)
.WithMany() .WithMany()
.HasForeignKey("UserId") .HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }

View File

@ -294,6 +294,31 @@ namespace Templates.Test.Helpers
} }
} }
internal async Task<ProcessEx> RunDotNetEfUpdateDatabaseAsync()
{
var assembly = typeof(ProjectFactoryFixture).Assembly;
var dotNetEfFullPath = assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
.First(attribute => attribute.Key == "DotNetEfFullPath")
.Value;
var args = $"\"{dotNetEfFullPath}\" --verbose --no-build database update";
// Only run one instance of 'dotnet new' at once, as a workaround for
// https://github.com/aspnet/templating/issues/63
await DotNetNewLock.WaitAsync();
try
{
var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), args);
await result.Exited;
return result;
}
finally
{
DotNetNewLock.Release();
}
}
// If this fails, you should generate new migrations via migrations/updateMigrations.cmd // If this fails, you should generate new migrations via migrations/updateMigrations.cmd
public void AssertEmptyMigration(string migration) public void AssertEmptyMigration(string migration)
{ {

View File

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.E2ETesting; using Microsoft.AspNetCore.E2ETesting;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -80,11 +81,20 @@ namespace Templates.Test.SpaTemplateTest
var buildResult = await Project.RunDotNetBuildAsync(); var buildResult = await Project.RunDotNetBuildAsync();
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult)); Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
// localdb is not installed on the CI machines, so skip it.
var shouldVisitFetchData = !useLocalDb;
if (usesAuth) if (usesAuth)
{ {
var migrationsResult = await Project.RunDotNetEfCreateMigrationAsync(template); var migrationsResult = await Project.RunDotNetEfCreateMigrationAsync(template);
Assert.True(0 == migrationsResult.ExitCode, ErrorMessages.GetFailedProcessMessage("run EF migrations", Project, migrationsResult)); Assert.True(0 == migrationsResult.ExitCode, ErrorMessages.GetFailedProcessMessage("run EF migrations", Project, migrationsResult));
Project.AssertEmptyMigration(template); Project.AssertEmptyMigration(template);
if (shouldVisitFetchData)
{
var dbUpdateResult = await Project.RunDotNetEfUpdateDatabaseAsync();
Assert.True(0 == dbUpdateResult.ExitCode, ErrorMessages.GetFailedProcessMessage("update database", Project, dbUpdateResult));
}
} }
using (var aspNetProcess = Project.StartBuiltProjectAsync()) using (var aspNetProcess = Project.StartBuiltProjectAsync())
@ -98,8 +108,9 @@ namespace Templates.Test.SpaTemplateTest
if (BrowserFixture.IsHostAutomationSupported()) if (BrowserFixture.IsHostAutomationSupported())
{ {
aspNetProcess.VisitInBrowser(Browser); var (browser, logs) = await BrowserFixture.GetOrCreateBrowserAsync(Output, $"{Project.ProjectName}.build");
TestBasicNavigation(visitFetchData: !usesAuth); aspNetProcess.VisitInBrowser(browser);
TestBasicNavigation(visitFetchData: shouldVisitFetchData, usesAuth, browser);
} }
} }
@ -119,8 +130,9 @@ namespace Templates.Test.SpaTemplateTest
if (BrowserFixture.IsHostAutomationSupported()) if (BrowserFixture.IsHostAutomationSupported())
{ {
aspNetProcess.VisitInBrowser(Browser); var (browser, logs) = await BrowserFixture.GetOrCreateBrowserAsync(Output, $"{Project.ProjectName}.publish");
TestBasicNavigation(visitFetchData: !usesAuth); aspNetProcess.VisitInBrowser(browser);
TestBasicNavigation(visitFetchData: shouldVisitFetchData, usesAuth, browser);
} }
} }
} }
@ -167,36 +179,52 @@ namespace Templates.Test.SpaTemplateTest
File.WriteAllText(Path.Combine(Project.TemplatePublishDir, "appsettings.json"), testAppSettings); File.WriteAllText(Path.Combine(Project.TemplatePublishDir, "appsettings.json"), testAppSettings);
} }
private void TestBasicNavigation(bool visitFetchData) private void TestBasicNavigation(bool visitFetchData, bool usesAuth, IWebDriver browser)
{ {
Browser.Exists(By.TagName("ul")); browser.Exists(By.TagName("ul"));
// <title> element gets project ID injected into it during template execution // <title> element gets project ID injected into it during template execution
Browser.Contains(Project.ProjectGuid, () => Browser.Title); browser.Contains(Project.ProjectGuid, () => browser.Title);
// Initially displays the home page // Initially displays the home page
Browser.Equal("Hello, world!", () => Browser.FindElement(By.TagName("h1")).Text); browser.Equal("Hello, world!", () => browser.FindElement(By.TagName("h1")).Text);
// Can navigate to the counter page // Can navigate to the counter page
Browser.FindElement(By.PartialLinkText("Counter")).Click(); browser.FindElement(By.PartialLinkText("Counter")).Click();
Browser.Contains("counter", () => Browser.Url); browser.Contains("counter", () => browser.Url);
Browser.Equal("Counter", () => Browser.FindElement(By.TagName("h1")).Text); browser.Equal("Counter", () => browser.FindElement(By.TagName("h1")).Text);
// Clicking the counter button works // Clicking the counter button works
Browser.Equal("0", () => Browser.FindElement(By.CssSelector("p>strong")).Text); browser.Equal("0", () => browser.FindElement(By.CssSelector("p>strong")).Text);
Browser.FindElement(By.CssSelector("p+button")).Click(); browser.FindElement(By.CssSelector("p+button")).Click();
Browser.Equal("1", () => Browser.FindElement(By.CssSelector("p>strong")).Text); browser.Equal("1", () => browser.FindElement(By.CssSelector("p>strong")).Text);
if (visitFetchData) if (visitFetchData)
{ {
browser.FindElement(By.PartialLinkText("Fetch data")).Click();
if (usesAuth)
{
// We will be redirected to the identity UI
browser.Contains("/Identity/Account/Login", () => browser.Url);
browser.FindElement(By.PartialLinkText("Register as a new user")).Click();
var userName = $"{Guid.NewGuid()}@example.com";
var password = $"!Test.Password1$";
browser.Exists(By.Name("Input.Email"));
browser.FindElement(By.Name("Input.Email")).SendKeys(userName);
browser.FindElement(By.Name("Input.Password")).SendKeys(password);
browser.FindElement(By.Name("Input.ConfirmPassword")).SendKeys(password);
browser.FindElement(By.Id("registerSubmit")).Click();
}
// Can navigate to the 'fetch data' page // Can navigate to the 'fetch data' page
Browser.FindElement(By.PartialLinkText("Fetch data")).Click(); browser.Contains("fetch-data", () => browser.Url);
Browser.Contains("fetch-data", () => Browser.Url); browser.Equal("Weather forecast", () => browser.FindElement(By.TagName("h1")).Text);
Browser.Equal("Weather forecast", () => Browser.FindElement(By.TagName("h1")).Text);
// Asynchronously loads and displays the table of weather forecasts // Asynchronously loads and displays the table of weather forecasts
Browser.Exists(By.CssSelector("table>tbody>tr")); browser.Exists(By.CssSelector("table>tbody>tr"));
Browser.Equal(5, () => Browser.FindElements(By.CssSelector("p+table>tbody>tr")).Count); browser.Equal(5, () => browser.FindElements(By.CssSelector("p+table>tbody>tr")).Count);
} }
} }

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,8 +16,8 @@ namespace Microsoft.AspNetCore.E2ETesting
{ {
public class BrowserFixture : IDisposable public class BrowserFixture : IDisposable
{ {
private RemoteWebDriver _browser; private ConcurrentDictionary<string, Task<(IWebDriver browser, ILogs log)>> _browsers = new ConcurrentDictionary<string, Task<(IWebDriver, ILogs)>>();
private RemoteLogs _logs; private List<IWebDriver> _browsersToDispose = new List<IWebDriver>();
public BrowserFixture(IMessageSink diagnosticsMessageSink) public BrowserFixture(IMessageSink diagnosticsMessageSink)
{ {
@ -52,22 +54,25 @@ namespace Microsoft.AspNetCore.E2ETesting
public void Dispose() public void Dispose()
{ {
_browser?.Dispose(); foreach (var browser in _browsersToDispose)
{
browser.Dispose();
}
} }
public async Task<(IWebDriver, ILogs)> GetOrCreateBrowserAsync(ITestOutputHelper output) public Task<(IWebDriver, ILogs)> GetOrCreateBrowserAsync(ITestOutputHelper output, string isolationContext = "")
{ {
if (!IsHostAutomationSupported()) if (!IsHostAutomationSupported())
{ {
output.WriteLine($"{nameof(BrowserFixture)}: Host does not support browser automation."); output.WriteLine($"{nameof(BrowserFixture)}: Host does not support browser automation.");
return default; return Task.FromResult<(IWebDriver, ILogs)>(default);
} }
if ((_browser, _logs) != (null, null)) return _browsers.GetOrAdd(isolationContext, CreateBrowserAsync, output);
{ }
return (_browser, _logs);
}
private async Task<(IWebDriver browser, ILogs log)> CreateBrowserAsync(string context, ITestOutputHelper output)
{
var opts = new ChromeOptions(); var opts = new ChromeOptions();
// Comment this out if you want to watch or interact with the browser (e.g., for debugging) // Comment this out if you want to watch or interact with the browser (e.g., for debugging)
@ -110,10 +115,8 @@ namespace Microsoft.AspNetCore.E2ETesting
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1); driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1);
var logs = new RemoteLogs(driver); var logs = new RemoteLogs(driver);
_browser = driver; _browsersToDispose.Add(driver);
_logs = logs; return (driver, logs);
return (_browser, _logs);
} }
catch catch
{ {