Add an end to end test for login in with a social provider.
This commit is contained in:
parent
5c105e6506
commit
d1aa7d527b
|
|
@ -234,7 +234,6 @@ Global
|
||||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x64.Build.0 = Debug|Any CPU
|
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
|
@ -282,7 +281,6 @@ Global
|
||||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x64.Build.0 = Debug|Any CPU
|
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x86.ActiveCfg = Debug|Any CPU
|
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Any CPU.Build.0 = Release|Any CPU
|
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<section>
|
<section>
|
||||||
<form method="post">
|
<form id="account" method="post">
|
||||||
<h4>Use a local account to log in.</h4>
|
<h4>Use a local account to log in.</h4>
|
||||||
<hr />
|
<hr />
|
||||||
<div asp-validation-summary="All" class="text-danger"></div>
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<form asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
|
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
@foreach (var provider in Model.ExternalLogins)
|
@foreach (var provider in Model.ExternalLogins)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Dom.Html;
|
using AngleSharp.Dom.Html;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -16,7 +14,27 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
public static Task<HttpResponseMessage> SendAsync(
|
public static Task<HttpResponseMessage> SendAsync(
|
||||||
this HttpClient client,
|
this HttpClient client,
|
||||||
IHtmlFormElement form,
|
IHtmlFormElement form,
|
||||||
IEnumerable<KeyValuePair<string,string>> formValues)
|
IHtmlElement submitButton)
|
||||||
|
{
|
||||||
|
return client.SendAsync(form, submitButton, new Dictionary<string, string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<HttpResponseMessage> SendAsync(
|
||||||
|
this HttpClient client,
|
||||||
|
IHtmlFormElement form,
|
||||||
|
IEnumerable<KeyValuePair<string, string>> formValues)
|
||||||
|
{
|
||||||
|
var submitElement = Assert.Single(form.QuerySelectorAll("[type=submit]"));
|
||||||
|
var submitButton = Assert.IsAssignableFrom<IHtmlElement>(submitElement);
|
||||||
|
|
||||||
|
return client.SendAsync(form, submitButton, formValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<HttpResponseMessage> SendAsync(
|
||||||
|
this HttpClient client,
|
||||||
|
IHtmlFormElement form,
|
||||||
|
IHtmlElement submitButton,
|
||||||
|
IEnumerable<KeyValuePair<string, string>> formValues)
|
||||||
{
|
{
|
||||||
foreach (var kvp in formValues)
|
foreach (var kvp in formValues)
|
||||||
{
|
{
|
||||||
|
|
@ -24,9 +42,6 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
element.Value = kvp.Value;
|
element.Value = kvp.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var submitElement = Assert.Single(form.QuerySelectorAll("[type=submit]"));
|
|
||||||
var submitButton = Assert.IsAssignableFrom<IHtmlElement>(submitElement);
|
|
||||||
|
|
||||||
var submit = form.GetSubmission(submitButton);
|
var submit = form.GetSubmission(submitButton);
|
||||||
var submision = new HttpRequestMessage(new HttpMethod(submit.Method.ToString()), submit.Target)
|
var submision = new HttpRequestMessage(new HttpMethod(submit.Method.ToString()), submit.Target)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
|
{
|
||||||
|
public class DefaultUIContext : HtmlPageContext
|
||||||
|
{
|
||||||
|
public DefaultUIContext()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultUIContext(DefaultUIContext currentContext)
|
||||||
|
: base(currentContext)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultUIContext WithAuthenticatedUser() =>
|
||||||
|
new DefaultUIContext(this) { UserAuthenticated = true };
|
||||||
|
|
||||||
|
public DefaultUIContext WithSocialLoginEnabled() =>
|
||||||
|
new DefaultUIContext(this) { ContosoLoginEnabled = true };
|
||||||
|
|
||||||
|
public DefaultUIContext WithExistingUser() =>
|
||||||
|
new DefaultUIContext(this) { ExistingUser = true };
|
||||||
|
|
||||||
|
public string AuthenticatorKey
|
||||||
|
{
|
||||||
|
get => GetValue<string>(nameof(AuthenticatorKey));
|
||||||
|
set => SetValue(nameof(AuthenticatorKey), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] RecoveryCodes
|
||||||
|
{
|
||||||
|
get => GetValue<string[]>(nameof(RecoveryCodes));
|
||||||
|
set => SetValue(nameof(RecoveryCodes), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TwoFactorEnabled
|
||||||
|
{
|
||||||
|
get => GetValue<bool>(nameof(TwoFactorEnabled));
|
||||||
|
set => SetValue(nameof(TwoFactorEnabled), value);
|
||||||
|
}
|
||||||
|
public bool ContosoLoginEnabled
|
||||||
|
{
|
||||||
|
get => GetValue<bool>(nameof(ContosoLoginEnabled));
|
||||||
|
set => SetValue(nameof(ContosoLoginEnabled), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UserAuthenticated
|
||||||
|
{
|
||||||
|
get => GetValue<bool>(nameof(UserAuthenticated));
|
||||||
|
set => SetValue(nameof(UserAuthenticated), value);
|
||||||
|
}
|
||||||
|
public bool ExistingUser
|
||||||
|
{
|
||||||
|
get => GetValue<bool>(nameof(ExistingUser));
|
||||||
|
set => SetValue(nameof(ExistingUser), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using AngleSharp.Dom.Html;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
|
{
|
||||||
|
public class DefaultUIPage : HtmlPage<DefaultUIContext>
|
||||||
|
{
|
||||||
|
public DefaultUIPage(HttpClient client, IHtmlDocument document, DefaultUIContext context)
|
||||||
|
: base(client, document, context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Identity.DefaultUI.WebSite;
|
||||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
@ -9,12 +10,13 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
{
|
{
|
||||||
public static class FunctionalTestsServiceCollectionExtensions
|
public static class FunctionalTestsServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection SetupTestDatabase(this IServiceCollection services, string databaseName)
|
public static IServiceCollection SetupTestDatabase(this IServiceCollection services, string databaseName) =>
|
||||||
{
|
|
||||||
services.AddDbContext<IdentityDbContext>(options =>
|
services.AddDbContext<IdentityDbContext>(options =>
|
||||||
options.UseInMemoryDatabase(databaseName, memoryOptions => { }));
|
options.UseInMemoryDatabase(databaseName, memoryOptions => { }));
|
||||||
|
|
||||||
return services;
|
public static IServiceCollection SetupTestThirdPartyLogin(this IServiceCollection services) =>
|
||||||
}
|
services.AddAuthentication()
|
||||||
|
.AddContosoAuthentication(o => o.SignInScheme = IdentityConstants.ExternalScheme)
|
||||||
|
.Services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ using AngleSharp.Dom.Html;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
{
|
{
|
||||||
public class HtmlPage
|
public class HtmlPage<TApplicationContext>
|
||||||
{
|
{
|
||||||
public HtmlPage(HttpClient client, IHtmlDocument document, HtmlPageContext context)
|
public HtmlPage(HttpClient client, IHtmlDocument document, TApplicationContext context)
|
||||||
{
|
{
|
||||||
Client = client;
|
Client = client;
|
||||||
Document = document;
|
Document = document;
|
||||||
|
|
@ -17,6 +17,6 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
|
|
||||||
public HttpClient Client { get; }
|
public HttpClient Client { get; }
|
||||||
public IHtmlDocument Document { get; }
|
public IHtmlDocument Document { get; }
|
||||||
public HtmlPageContext Context { get; }
|
public TApplicationContext Context { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,26 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
{
|
{
|
||||||
public class HtmlPageContext
|
public class HtmlPageContext
|
||||||
{
|
{
|
||||||
private readonly IDictionary<string, string> _properties =
|
private readonly IDictionary<string, object> _properties;
|
||||||
new Dictionary<string, string>();
|
|
||||||
|
|
||||||
public string this[string key]
|
protected HtmlPageContext()
|
||||||
|
: this(new Dictionary<string, object>())
|
||||||
{
|
{
|
||||||
get => _properties[key];
|
|
||||||
set => _properties[key] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected HtmlPageContext(HtmlPageContext currentContext)
|
||||||
|
: this(new Dictionary<string,object>(currentContext._properties))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private HtmlPageContext(IDictionary<string, object> properties)
|
||||||
|
{
|
||||||
|
_properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TValue GetValue<TValue>(string key) =>
|
||||||
|
_properties.TryGetValue(key, out var rawValue) ? (TValue)rawValue : default;
|
||||||
|
protected void SetValue(string key, object value) =>
|
||||||
|
_properties[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Dom.Html;
|
using AngleSharp.Dom.Html;
|
||||||
using Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage;
|
|
||||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -45,9 +44,9 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
var password = $"!Test.Password1$";
|
var password = $"!Test.Password1$";
|
||||||
|
|
||||||
var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password);
|
var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password);
|
||||||
var showRecoveryCodes = await UserStories.EnableTwoFactorAuthentication(loggedIn, twoFactorEnabled: false);
|
var showRecoveryCodes = await UserStories.EnableTwoFactorAuthentication(loggedIn);
|
||||||
|
|
||||||
var twoFactorKey = showRecoveryCodes.Context[EnableAuthenticator.AuthenticatorKey];
|
var twoFactorKey = showRecoveryCodes.Context.AuthenticatorKey;
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
// Use a new client to simulate a new browser session.
|
// Use a new client to simulate a new browser session.
|
||||||
|
|
@ -66,11 +65,9 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
var password = $"!Test.Password1$";
|
var password = $"!Test.Password1$";
|
||||||
|
|
||||||
var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password);
|
var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password);
|
||||||
var showRecoveryCodes = await UserStories.EnableTwoFactorAuthentication(loggedIn, twoFactorEnabled: false);
|
var showRecoveryCodes = await UserStories.EnableTwoFactorAuthentication(loggedIn);
|
||||||
|
|
||||||
var recoveryCode = showRecoveryCodes.Context[ShowRecoveryCodes.RecoveryCodes]
|
var recoveryCode = showRecoveryCodes.Context.RecoveryCodes.First();
|
||||||
.Split(' ')
|
|
||||||
.First();
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
// Use a new client to simulate a new browser session.
|
// Use a new client to simulate a new browser session.
|
||||||
|
|
@ -131,6 +128,24 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
|
|
||||||
await UserStories.LoginExistingUserAsync(newClient, userName, password);
|
await UserStories.LoginExistingUserAsync(newClient, userName, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CanLoginWithASocialLoginProvider()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = ServerFactory.CreateServer(builder =>
|
||||||
|
builder.ConfigureServices(services => services.SetupTestThirdPartyLogin()));
|
||||||
|
var client = ServerFactory.CreateDefaultClient(server);
|
||||||
|
var newClient = ServerFactory.CreateDefaultClient(server);
|
||||||
|
|
||||||
|
var guid = Guid.NewGuid();
|
||||||
|
var userName = $"{guid}";
|
||||||
|
var email = $"{guid}@example.com";
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
await UserStories.RegisterNewUserWithSocialLoginAsync(client, userName, email);
|
||||||
|
await UserStories.LoginWithSocialLoginAsync(newClient, userName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestEmailSender : IEmailSender
|
class TestEmailSender : IEmailSender
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
var index = await UserStories.RegisterNewUserAsync(client, userName, password);
|
var index = await UserStories.RegisterNewUserAsync(client, userName, password);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await UserStories.EnableTwoFactorAuthentication(index, twoFactorEnabled: false);
|
await UserStories.EnableTwoFactorAuthentication(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AngleSharp.Dom.Html;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Pages.Account
|
||||||
|
{
|
||||||
|
public class ExternalLogin : DefaultUIPage
|
||||||
|
{
|
||||||
|
private readonly IHtmlFormElement _emailForm;
|
||||||
|
|
||||||
|
public ExternalLogin(
|
||||||
|
HttpClient client,
|
||||||
|
IHtmlDocument externalLogin,
|
||||||
|
DefaultUIContext context)
|
||||||
|
: base(client, externalLogin, context)
|
||||||
|
{
|
||||||
|
_emailForm = HtmlAssert.HasForm(Document);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Index> SendEmailAsync(string email)
|
||||||
|
{
|
||||||
|
var response = await Client.SendAsync(_emailForm, new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["Input_Email"] = email
|
||||||
|
});
|
||||||
|
var goToIndex = ResponseAssert.IsRedirect(response);
|
||||||
|
var indexResponse = await Client.GetAsync(goToIndex);
|
||||||
|
var index = await ResponseAssert.IsHtmlDocumentAsync(indexResponse);
|
||||||
|
|
||||||
|
return new Index(Client, index, Context.WithAuthenticatedUser());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,14 +9,35 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||||
{
|
{
|
||||||
public class Login : HtmlPage
|
public class Login : DefaultUIPage
|
||||||
{
|
{
|
||||||
private readonly IHtmlFormElement _loginForm;
|
private readonly IHtmlFormElement _loginForm;
|
||||||
|
private readonly IHtmlFormElement _externalLoginForm;
|
||||||
|
private readonly IHtmlElement _contosoButton;
|
||||||
|
|
||||||
public Login(HttpClient client, IHtmlDocument login, HtmlPageContext context)
|
public Login(
|
||||||
|
HttpClient client,
|
||||||
|
IHtmlDocument login,
|
||||||
|
DefaultUIContext context)
|
||||||
: base(client, login, context)
|
: base(client, login, context)
|
||||||
{
|
{
|
||||||
_loginForm = HtmlAssert.HasForm(login);
|
_loginForm = HtmlAssert.HasForm("#account", login);
|
||||||
|
if (Context.ContosoLoginEnabled)
|
||||||
|
{
|
||||||
|
_externalLoginForm = HtmlAssert.HasForm("#external-account", login);
|
||||||
|
_contosoButton = HtmlAssert.HasElement("button[value=Contoso]", login);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Contoso.Login> ClickLoginWithContosoLinkAsync()
|
||||||
|
{
|
||||||
|
var externalFormResponse = await Client.SendAsync(_externalLoginForm, _contosoButton);
|
||||||
|
var goToContosoLogin = ResponseAssert.IsRedirect(externalFormResponse);
|
||||||
|
var contosoLoginResponse = await Client.GetAsync(goToContosoLogin);
|
||||||
|
|
||||||
|
var contosoLogin = await ResponseAssert.IsHtmlDocumentAsync(contosoLoginResponse);
|
||||||
|
|
||||||
|
return new Contoso.Login(Client, contosoLogin, Context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Index> LoginValidUserAsync(string userName, string password)
|
public async Task<Index> LoginValidUserAsync(string userName, string password)
|
||||||
|
|
@ -27,7 +48,10 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||||
Assert.Equal(Index.Path, loggedInLocation.ToString());
|
Assert.Equal(Index.Path, loggedInLocation.ToString());
|
||||||
var indexResponse = await Client.GetAsync(loggedInLocation);
|
var indexResponse = await Client.GetAsync(loggedInLocation);
|
||||||
var index = await ResponseAssert.IsHtmlDocumentAsync(indexResponse);
|
var index = await ResponseAssert.IsHtmlDocumentAsync(indexResponse);
|
||||||
return new Index(Client, index, Context, authenticated: true);
|
return new Index(
|
||||||
|
Client,
|
||||||
|
index,
|
||||||
|
Context.WithAuthenticatedUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> SendLoginForm(string userName, string password)
|
private async Task<HttpResponseMessage> SendLoginForm(string userName, string password)
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||||
{
|
{
|
||||||
public class LoginWith2fa : HtmlPage
|
public class LoginWith2fa : DefaultUIPage
|
||||||
{
|
{
|
||||||
public const string Path = "/Identity/Account/LoginWith2fa";
|
public const string Path = "/Identity/Account/LoginWith2fa";
|
||||||
|
|
||||||
private readonly IHtmlFormElement _twoFactorForm;
|
private readonly IHtmlFormElement _twoFactorForm;
|
||||||
private readonly IHtmlAnchorElement _loginWithRecoveryCodeLink;
|
private readonly IHtmlAnchorElement _loginWithRecoveryCodeLink;
|
||||||
|
|
||||||
public LoginWith2fa(HttpClient client, IHtmlDocument loginWithTwoFactor, HtmlPageContext context)
|
public LoginWith2fa(HttpClient client, IHtmlDocument loginWithTwoFactor, DefaultUIContext context)
|
||||||
: base(client, loginWithTwoFactor, context)
|
: base(client, loginWithTwoFactor, context)
|
||||||
{
|
{
|
||||||
_twoFactorForm = HtmlAssert.HasForm(loginWithTwoFactor);
|
_twoFactorForm = HtmlAssert.HasForm(loginWithTwoFactor);
|
||||||
|
|
@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||||
var indexResponse = await Client.GetAsync(goToIndex);
|
var indexResponse = await Client.GetAsync(goToIndex);
|
||||||
var index = await ResponseAssert.IsHtmlDocumentAsync(indexResponse);
|
var index = await ResponseAssert.IsHtmlDocumentAsync(indexResponse);
|
||||||
|
|
||||||
return new Index(Client, index, Context, true);
|
return new Index(Client, index, Context.WithAuthenticatedUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<LoginWithRecoveryCode> ClickRecoveryCodeLinkAsync()
|
internal async Task<LoginWithRecoveryCode> ClickRecoveryCodeLinkAsync()
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ using AngleSharp.Dom.Html;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||||
{
|
{
|
||||||
public class LoginWithRecoveryCode : HtmlPage
|
public class LoginWithRecoveryCode : DefaultUIPage
|
||||||
{
|
{
|
||||||
private readonly IHtmlFormElement _loginWithRecoveryCodeForm;
|
private readonly IHtmlFormElement _loginWithRecoveryCodeForm;
|
||||||
|
|
||||||
public LoginWithRecoveryCode(HttpClient client, IHtmlDocument loginWithRecoveryCode, HtmlPageContext context)
|
public LoginWithRecoveryCode(HttpClient client, IHtmlDocument loginWithRecoveryCode, DefaultUIContext context)
|
||||||
: base(client, loginWithRecoveryCode, context)
|
: base(client, loginWithRecoveryCode, context)
|
||||||
{
|
{
|
||||||
_loginWithRecoveryCodeForm = HtmlAssert.HasForm(loginWithRecoveryCode);
|
_loginWithRecoveryCodeForm = HtmlAssert.HasForm(loginWithRecoveryCode);
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||||
var indexPage = await Client.GetAsync(goToIndex);
|
var indexPage = await Client.GetAsync(goToIndex);
|
||||||
var index = await ResponseAssert.IsHtmlDocumentAsync(indexPage);
|
var index = await ResponseAssert.IsHtmlDocumentAsync(indexPage);
|
||||||
|
|
||||||
return new Index(Client, index, Context, authenticated: true);
|
return new Index(Client, index, new DefaultUIContext(Context) { UserAuthenticated = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,19 +7,24 @@ using System.Net.Http;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Dom.Html;
|
using AngleSharp.Dom.Html;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
||||||
{
|
{
|
||||||
internal class EnableAuthenticator : HtmlPage
|
internal class EnableAuthenticator : DefaultUIPage
|
||||||
{
|
{
|
||||||
public const string AuthenticatorKey = nameof(EnableAuthenticator) + "." + nameof(AuthenticatorKey);
|
public const string AuthenticatorKey = nameof(EnableAuthenticator) + "." + nameof(AuthenticatorKey);
|
||||||
|
|
||||||
private readonly IHtmlElement _codeElement;
|
private readonly IHtmlElement _codeElement;
|
||||||
private readonly IHtmlFormElement _sendCodeForm;
|
private readonly IHtmlFormElement _sendCodeForm;
|
||||||
|
|
||||||
public EnableAuthenticator(HttpClient client, IHtmlDocument enableAuthenticator, HtmlPageContext context)
|
public EnableAuthenticator(
|
||||||
|
HttpClient client,
|
||||||
|
IHtmlDocument enableAuthenticator,
|
||||||
|
DefaultUIContext context)
|
||||||
: base(client, enableAuthenticator, context)
|
: base(client, enableAuthenticator, context)
|
||||||
{
|
{
|
||||||
|
Assert.True(Context.UserAuthenticated);
|
||||||
_codeElement = HtmlAssert.HasElement("kbd", enableAuthenticator);
|
_codeElement = HtmlAssert.HasElement("kbd", enableAuthenticator);
|
||||||
_sendCodeForm = HtmlAssert.HasForm("#send-code", enableAuthenticator);
|
_sendCodeForm = HtmlAssert.HasForm("#send-code", enableAuthenticator);
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +32,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
||||||
internal async Task<ShowRecoveryCodes> SendValidCodeAsync()
|
internal async Task<ShowRecoveryCodes> SendValidCodeAsync()
|
||||||
{
|
{
|
||||||
var authenticatorKey = _codeElement.TextContent.Replace(" ", "");
|
var authenticatorKey = _codeElement.TextContent.Replace(" ", "");
|
||||||
Context[AuthenticatorKey] = authenticatorKey;
|
Context.AuthenticatorKey = authenticatorKey;
|
||||||
var verificationCode = ComputeCode(authenticatorKey);
|
var verificationCode = ComputeCode(authenticatorKey);
|
||||||
|
|
||||||
var sendCodeResponse = await Client.SendAsync(_sendCodeForm, new Dictionary<string, string>
|
var sendCodeResponse = await Client.SendAsync(_sendCodeForm, new Dictionary<string, string>
|
||||||
|
|
|
||||||
|
|
@ -4,31 +4,34 @@
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Dom.Html;
|
using AngleSharp.Dom.Html;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
||||||
{
|
{
|
||||||
public class Index : HtmlPage
|
public class Index : DefaultUIPage
|
||||||
{
|
{
|
||||||
private readonly IHtmlAnchorElement _profileLink;
|
private readonly IHtmlAnchorElement _profileLink;
|
||||||
private readonly IHtmlAnchorElement _changePasswordLink;
|
private readonly IHtmlAnchorElement _changePasswordLink;
|
||||||
private readonly IHtmlAnchorElement _twoFactorLink;
|
private readonly IHtmlAnchorElement _twoFactorLink;
|
||||||
private readonly IHtmlAnchorElement _personalDataLink;
|
private readonly IHtmlAnchorElement _personalDataLink;
|
||||||
|
|
||||||
public Index(HttpClient client, IHtmlDocument manage, HtmlPageContext context)
|
public Index(HttpClient client, IHtmlDocument manage, DefaultUIContext context)
|
||||||
: base(client, manage, context)
|
: base(client, manage, context)
|
||||||
{
|
{
|
||||||
|
Assert.True(Context.UserAuthenticated);
|
||||||
|
|
||||||
_profileLink = HtmlAssert.HasLink("#profile", manage);
|
_profileLink = HtmlAssert.HasLink("#profile", manage);
|
||||||
_changePasswordLink = HtmlAssert.HasLink("#change-password", manage);
|
_changePasswordLink = HtmlAssert.HasLink("#change-password", manage);
|
||||||
_twoFactorLink = HtmlAssert.HasLink("#two-factor", manage);
|
_twoFactorLink = HtmlAssert.HasLink("#two-factor", manage);
|
||||||
_personalDataLink = HtmlAssert.HasLink("#personal-data", manage);
|
_personalDataLink = HtmlAssert.HasLink("#personal-data", manage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TwoFactorAuthentication> ClickTwoFactorLinkAsync(bool twoFactorEnabled)
|
public async Task<TwoFactorAuthentication> ClickTwoFactorLinkAsync()
|
||||||
{
|
{
|
||||||
var goToTwoFactor = await Client.GetAsync(_twoFactorLink.Href);
|
var goToTwoFactor = await Client.GetAsync(_twoFactorLink.Href);
|
||||||
var twoFactor = await ResponseAssert.IsHtmlDocumentAsync(goToTwoFactor);
|
var twoFactor = await ResponseAssert.IsHtmlDocumentAsync(goToTwoFactor);
|
||||||
|
|
||||||
return new TwoFactorAuthentication(Client, twoFactor, Context, twoFactorEnabled);
|
return new TwoFactorAuthentication(Client, twoFactor, Context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,15 @@ using AngleSharp.Dom.Html;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
||||||
{
|
{
|
||||||
internal class ShowRecoveryCodes : HtmlPage
|
internal class ShowRecoveryCodes : DefaultUIPage
|
||||||
{
|
{
|
||||||
public const string RecoveryCodes = nameof(ShowRecoveryCodes) + "." + nameof(RecoveryCodes);
|
|
||||||
|
|
||||||
private readonly IEnumerable<IHtmlElement> _recoveryCodeElements;
|
private readonly IEnumerable<IHtmlElement> _recoveryCodeElements;
|
||||||
|
|
||||||
public ShowRecoveryCodes(HttpClient client, IHtmlDocument showRecoveryCodes, HtmlPageContext context)
|
public ShowRecoveryCodes(HttpClient client, IHtmlDocument showRecoveryCodes, DefaultUIContext context)
|
||||||
: base(client, showRecoveryCodes, context)
|
: base(client, showRecoveryCodes, context)
|
||||||
{
|
{
|
||||||
_recoveryCodeElements = HtmlAssert.HasElements(".recovery-code", showRecoveryCodes);
|
_recoveryCodeElements = HtmlAssert.HasElements(".recovery-code", showRecoveryCodes);
|
||||||
Context[RecoveryCodes] = string.Join(" ", Codes);
|
Context.RecoveryCodes = Codes.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> Codes => _recoveryCodeElements.Select(rc => rc.TextContent);
|
public IEnumerable<string> Codes => _recoveryCodeElements.Select(rc => rc.TextContent);
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,14 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
||||||
{
|
{
|
||||||
public class TwoFactorAuthentication : HtmlPage
|
public class TwoFactorAuthentication : DefaultUIPage
|
||||||
{
|
{
|
||||||
private readonly bool _twoFactorEnabled;
|
|
||||||
private readonly IHtmlAnchorElement _enableAuthenticatorLink;
|
private readonly IHtmlAnchorElement _enableAuthenticatorLink;
|
||||||
|
|
||||||
public TwoFactorAuthentication(HttpClient client, IHtmlDocument twoFactor, HtmlPageContext context, bool twoFactorEnabled)
|
public TwoFactorAuthentication(HttpClient client, IHtmlDocument twoFactor, DefaultUIContext context)
|
||||||
: base(client, twoFactor, context)
|
: base(client, twoFactor, context)
|
||||||
{
|
{
|
||||||
_twoFactorEnabled = twoFactorEnabled;
|
if (!Context.TwoFactorEnabled)
|
||||||
if (!_twoFactorEnabled)
|
|
||||||
{
|
{
|
||||||
_enableAuthenticatorLink = HtmlAssert.HasLink("#enable-authenticator", twoFactor);
|
_enableAuthenticatorLink = HtmlAssert.HasLink("#enable-authenticator", twoFactor);
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +23,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
|
||||||
|
|
||||||
internal async Task<EnableAuthenticator> ClickEnableAuthenticatorLinkAsync()
|
internal async Task<EnableAuthenticator> ClickEnableAuthenticatorLinkAsync()
|
||||||
{
|
{
|
||||||
Assert.False(_twoFactorEnabled);
|
Assert.False(Context.TwoFactorEnabled);
|
||||||
|
|
||||||
var goToEnableAuthenticator = await Client.GetAsync(_enableAuthenticatorLink.Href);
|
var goToEnableAuthenticator = await Client.GetAsync(_enableAuthenticatorLink.Href);
|
||||||
var enableAuthenticator = await ResponseAssert.IsHtmlDocumentAsync(goToEnableAuthenticator);
|
var enableAuthenticator = await ResponseAssert.IsHtmlDocumentAsync(goToEnableAuthenticator);
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||||
{
|
{
|
||||||
public class Register : HtmlPage
|
public class Register : DefaultUIPage
|
||||||
{
|
{
|
||||||
private IHtmlFormElement _registerForm;
|
private IHtmlFormElement _registerForm;
|
||||||
|
|
||||||
public Register(HttpClient client, IHtmlDocument register, HtmlPageContext context)
|
public Register(HttpClient client, IHtmlDocument register, DefaultUIContext context)
|
||||||
: base(client, register, context)
|
: base(client, register, context)
|
||||||
{
|
{
|
||||||
_registerForm = HtmlAssert.HasForm(register);
|
_registerForm = HtmlAssert.HasForm(register);
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||||
var indexResponse = await Client.GetAsync(registeredLocation);
|
var indexResponse = await Client.GetAsync(registeredLocation);
|
||||||
var index = await ResponseAssert.IsHtmlDocumentAsync(indexResponse);
|
var index = await ResponseAssert.IsHtmlDocumentAsync(indexResponse);
|
||||||
|
|
||||||
return new Index(Client, index, Context, authenticated: true);
|
return new Index(Client, index, Context.WithAuthenticatedUser());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AngleSharp.Dom.Html;
|
||||||
|
using Microsoft.AspNetCore.Identity.FunctionalTests.Pages.Account;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Contoso
|
||||||
|
{
|
||||||
|
public class Login : DefaultUIPage
|
||||||
|
{
|
||||||
|
private readonly IHtmlFormElement _loginForm;
|
||||||
|
|
||||||
|
public Login(HttpClient client, IHtmlDocument login, DefaultUIContext context)
|
||||||
|
: base(client, login, context)
|
||||||
|
{
|
||||||
|
_loginForm = HtmlAssert.HasForm(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ExternalLogin> SendNewUserNameAsync(string userName)
|
||||||
|
{
|
||||||
|
var externalLogin = await SendLoginForm(userName);
|
||||||
|
|
||||||
|
return new ExternalLogin(Client, externalLogin, Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Index> SendExistingUserNameAsync(string userName)
|
||||||
|
{
|
||||||
|
var externalLogin = await SendLoginForm(userName);
|
||||||
|
|
||||||
|
return new Index(Client, externalLogin, Context.WithAuthenticatedUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IHtmlDocument> SendLoginForm(string userName)
|
||||||
|
{
|
||||||
|
var contosoResponse = await Client.SendAsync(_loginForm, new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["Input_Login"] = userName
|
||||||
|
});
|
||||||
|
|
||||||
|
var goToExternalLogin = ResponseAssert.IsRedirect(contosoResponse);
|
||||||
|
var externalLogInResponse = await Client.GetAsync(goToExternalLogin);
|
||||||
|
if (Context.ExistingUser)
|
||||||
|
{
|
||||||
|
var goToIndex = ResponseAssert.IsRedirect(externalLogInResponse);
|
||||||
|
var indexResponse = await Client.GetAsync(goToIndex);
|
||||||
|
return await ResponseAssert.IsHtmlDocumentAsync(indexResponse);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return await ResponseAssert.IsHtmlDocumentAsync(externalLogInResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,19 +9,20 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
{
|
{
|
||||||
public class Index : HtmlPage
|
public class Index : DefaultUIPage
|
||||||
{
|
{
|
||||||
private readonly bool _authenticated;
|
|
||||||
private readonly IHtmlAnchorElement _registerLink;
|
private readonly IHtmlAnchorElement _registerLink;
|
||||||
private readonly IHtmlAnchorElement _loginLink;
|
private readonly IHtmlAnchorElement _loginLink;
|
||||||
private readonly IHtmlAnchorElement _manageLink;
|
private readonly IHtmlAnchorElement _manageLink;
|
||||||
public static readonly string Path = "/";
|
public static readonly string Path = "/";
|
||||||
|
|
||||||
public Index(HttpClient client, IHtmlDocument index, HtmlPageContext context, bool authenticated)
|
public Index(
|
||||||
|
HttpClient client,
|
||||||
|
IHtmlDocument index,
|
||||||
|
DefaultUIContext context)
|
||||||
: base(client, index, context)
|
: base(client, index, context)
|
||||||
{
|
{
|
||||||
_authenticated = authenticated;
|
if (!Context.UserAuthenticated)
|
||||||
if (!_authenticated)
|
|
||||||
{
|
{
|
||||||
_registerLink = HtmlAssert.HasLink("#register", Document);
|
_registerLink = HtmlAssert.HasLink("#register", Document);
|
||||||
_loginLink = HtmlAssert.HasLink("#login", Document);
|
_loginLink = HtmlAssert.HasLink("#login", Document);
|
||||||
|
|
@ -32,17 +33,17 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Index> CreateAsync(HttpClient client, bool authenticated = false)
|
public static async Task<Index> CreateAsync(HttpClient client, DefaultUIContext context = null)
|
||||||
{
|
{
|
||||||
var goToIndex = await client.GetAsync("/");
|
var goToIndex = await client.GetAsync("/");
|
||||||
var index = await ResponseAssert.IsHtmlDocumentAsync(goToIndex);
|
var index = await ResponseAssert.IsHtmlDocumentAsync(goToIndex);
|
||||||
|
|
||||||
return new Index(client, index, new HtmlPageContext(), authenticated);
|
return new Index(client, index, context ?? new DefaultUIContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Register> ClickRegisterLinkAsync()
|
public async Task<Register> ClickRegisterLinkAsync()
|
||||||
{
|
{
|
||||||
Assert.False(_authenticated);
|
Assert.False(Context.UserAuthenticated);
|
||||||
|
|
||||||
var goToRegister = await Client.GetAsync(_registerLink.Href);
|
var goToRegister = await Client.GetAsync(_registerLink.Href);
|
||||||
var register = await ResponseAssert.IsHtmlDocumentAsync(goToRegister);
|
var register = await ResponseAssert.IsHtmlDocumentAsync(goToRegister);
|
||||||
|
|
@ -52,7 +53,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
|
|
||||||
public async Task<Login> ClickLoginLinkAsync()
|
public async Task<Login> ClickLoginLinkAsync()
|
||||||
{
|
{
|
||||||
Assert.False(_authenticated);
|
Assert.False(Context.UserAuthenticated);
|
||||||
|
|
||||||
var goToLogin = await Client.GetAsync(_loginLink.Href);
|
var goToLogin = await Client.GetAsync(_loginLink.Href);
|
||||||
var login = await ResponseAssert.IsHtmlDocumentAsync(goToLogin);
|
var login = await ResponseAssert.IsHtmlDocumentAsync(goToLogin);
|
||||||
|
|
@ -62,7 +63,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
|
|
||||||
internal async Task<Account.Manage.Index> ClickManageLinkAsync()
|
internal async Task<Account.Manage.Index> ClickManageLinkAsync()
|
||||||
{
|
{
|
||||||
Assert.True(_authenticated);
|
Assert.True(Context.UserAuthenticated);
|
||||||
|
|
||||||
var goToManage = await Client.GetAsync(_manageLink.Href);
|
var goToManage = await Client.GetAsync(_manageLink.Href);
|
||||||
var manage = await ResponseAssert.IsHtmlDocumentAsync(goToManage);
|
var manage = await ResponseAssert.IsHtmlDocumentAsync(goToManage);
|
||||||
|
|
|
||||||
|
|
@ -21,5 +21,21 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await UserStories.RegisterNewUserAsync(client, userName, password);
|
await UserStories.RegisterNewUserAsync(client, userName, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CanRegisterWithASocialLoginProvider()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = ServerFactory.CreateServer(builder =>
|
||||||
|
builder.ConfigureServices(services => services.SetupTestThirdPartyLogin()));
|
||||||
|
var client = ServerFactory.CreateDefaultClient(server);
|
||||||
|
|
||||||
|
var guid = Guid.NewGuid();
|
||||||
|
var userName = $"{guid}";
|
||||||
|
var email = $"{guid}@example.com";
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
await UserStories.RegisterNewUserWithSocialLoginAsync(client, userName, email);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,34 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
return await login.LoginValidUserAsync(userName, password);
|
return await login.LoginValidUserAsync(userName, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static async Task<Index> RegisterNewUserWithSocialLoginAsync(HttpClient client, string userName, string email)
|
||||||
|
{
|
||||||
|
var index = await Index.CreateAsync(client,new DefaultUIContext().WithSocialLoginEnabled());
|
||||||
|
|
||||||
|
var login = await index.ClickLoginLinkAsync();
|
||||||
|
|
||||||
|
var contosoLogin = await login.ClickLoginWithContosoLinkAsync();
|
||||||
|
|
||||||
|
var externalLogin = await contosoLogin.SendNewUserNameAsync(userName);
|
||||||
|
|
||||||
|
return await externalLogin.SendEmailAsync(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task<Index> LoginWithSocialLoginAsync(HttpClient client, string userName)
|
||||||
|
{
|
||||||
|
var index = await Index.CreateAsync(
|
||||||
|
client,
|
||||||
|
new DefaultUIContext()
|
||||||
|
.WithSocialLoginEnabled()
|
||||||
|
.WithExistingUser());
|
||||||
|
|
||||||
|
var login = await index.ClickLoginLinkAsync();
|
||||||
|
|
||||||
|
var contosoLogin = await login.ClickLoginWithContosoLinkAsync();
|
||||||
|
|
||||||
|
return await contosoLogin.SendExistingUserNameAsync(userName);
|
||||||
|
}
|
||||||
|
|
||||||
internal static async Task<Index> LoginExistingUser2FaAsync(HttpClient client, string userName, string password, string twoFactorKey)
|
internal static async Task<Index> LoginExistingUser2FaAsync(HttpClient client, string userName, string password, string twoFactorKey)
|
||||||
{
|
{
|
||||||
var index = await Index.CreateAsync(client);
|
var index = await Index.CreateAsync(client);
|
||||||
|
|
@ -41,12 +69,10 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
return await login2Fa.Send2FACodeAsync(twoFactorKey);
|
return await login2Fa.Send2FACodeAsync(twoFactorKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task<ShowRecoveryCodes> EnableTwoFactorAuthentication(
|
internal static async Task<ShowRecoveryCodes> EnableTwoFactorAuthentication(Index index)
|
||||||
Index index,
|
|
||||||
bool twoFactorEnabled)
|
|
||||||
{
|
{
|
||||||
var manage = await index.ClickManageLinkAsync();
|
var manage = await index.ClickManageLinkAsync();
|
||||||
var twoFactor = await manage.ClickTwoFactorLinkAsync(twoFactorEnabled);
|
var twoFactor = await manage.ClickTwoFactorLinkAsync();
|
||||||
var enableAuthenticator = await twoFactor.ClickEnableAuthenticatorLinkAsync();
|
var enableAuthenticator = await twoFactor.ClickEnableAuthenticatorLinkAsync();
|
||||||
return await enableAuthenticator.SendValidCodeAsync();
|
return await enableAuthenticator.SendValidCodeAsync();
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +89,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
||||||
|
|
||||||
var login2Fa = await loginWithPassword.PasswordLoginValidUserWith2FaAsync(userName, password);
|
var login2Fa = await loginWithPassword.PasswordLoginValidUserWith2FaAsync(userName, password);
|
||||||
|
|
||||||
var loginRecoveryCode = await login2Fa.ClickRecoveryCodeLinkAsync();
|
var loginRecoveryCode = await login2Fa.ClickRecoveryCodeLinkAsync();
|
||||||
|
|
||||||
return await loginRecoveryCode.SendRecoveryCodeAsync(recoveryCode);
|
return await loginRecoveryCode.SendRecoveryCodeAsync(recoveryCode);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
231
test/WebSites/Identity.DefaultUI.WebSite/Data/Migrations/20180217170630_UpdateIdentitySchema.Designer.cs
generated
Normal file
231
test/WebSites/Identity.DefaultUI.WebSite/Data/Migrations/20180217170630_UpdateIdentitySchema.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||||
|
|
||||||
|
namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(IdentityDbContext))]
|
||||||
|
[Migration("20180217170630_UpdateIdentitySchema")]
|
||||||
|
partial class UpdateIdentitySchema
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.1.0-preview2-30103")
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
|
{
|
||||||
|
public partial class UpdateIdentitySchema : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_AspNetUserRoles_UserId",
|
||||||
|
table: "AspNetUserRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedUserName",
|
||||||
|
unique: true,
|
||||||
|
filter: "[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles",
|
||||||
|
column: "NormalizedName",
|
||||||
|
unique: true,
|
||||||
|
filter: "[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||||
|
table: "AspNetUserTokens",
|
||||||
|
column: "UserId",
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||||
|
table: "AspNetUserTokens");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedUserName",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserRoles_UserId",
|
||||||
|
table: "AspNetUserRoles",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles",
|
||||||
|
column: "NormalizedName");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// <auto-generated />
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||||
|
|
||||||
namespace Identity.DefaultUI.WebSite.Data.Migrations
|
namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
{
|
{
|
||||||
|
|
@ -14,32 +16,36 @@ namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
{
|
{
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "1.0.0-rc3")
|
.HasAnnotation("ProductVersion", "2.1.0-preview2-30103")
|
||||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.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();
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.HasAnnotation("MaxLength", 256);
|
.HasMaxLength(256);
|
||||||
|
|
||||||
b.Property<string>("NormalizedName")
|
b.Property<string>("NormalizedName")
|
||||||
.HasAnnotation("MaxLength", 256);
|
.HasMaxLength(256);
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("NormalizedName")
|
b.HasIndex("NormalizedName")
|
||||||
.HasName("RoleNameIndex");
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
b.ToTable("AspNetRoles");
|
b.ToTable("AspNetRoles");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd();
|
.ValueGeneratedOnAdd();
|
||||||
|
|
@ -58,7 +64,58 @@ namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
b.ToTable("AspNetRoleClaims");
|
b.ToTable("AspNetRoleClaims");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd();
|
.ValueGeneratedOnAdd();
|
||||||
|
|
@ -77,7 +134,7 @@ namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
b.ToTable("AspNetUserClaims");
|
b.ToTable("AspNetUserClaims");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("LoginProvider");
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
|
@ -95,7 +152,7 @@ namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
b.ToTable("AspNetUserLogins");
|
b.ToTable("AspNetUserLogins");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("UserId");
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
|
@ -105,12 +162,10 @@ namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("AspNetUserRoles");
|
b.ToTable("AspNetUserRoles");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("UserId");
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
|
@ -125,91 +180,51 @@ namespace Identity.DefaultUI.WebSite.Data.Migrations
|
||||||
b.ToTable("AspNetUserTokens");
|
b.ToTable("AspNetUserTokens");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id");
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
b.Property<int>("AccessFailedCount");
|
|
||||||
|
|
||||||
b.Property<string>("ConcurrencyStamp")
|
|
||||||
.IsConcurrencyToken();
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.HasAnnotation("MaxLength", 256);
|
|
||||||
|
|
||||||
b.Property<bool>("EmailConfirmed");
|
|
||||||
|
|
||||||
b.Property<bool>("LockoutEnabled");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
|
||||||
|
|
||||||
b.Property<string>("NormalizedEmail")
|
|
||||||
.HasAnnotation("MaxLength", 256);
|
|
||||||
|
|
||||||
b.Property<string>("NormalizedUserName")
|
|
||||||
.HasAnnotation("MaxLength", 256);
|
|
||||||
|
|
||||||
b.Property<string>("PasswordHash");
|
|
||||||
|
|
||||||
b.Property<string>("PhoneNumber");
|
|
||||||
|
|
||||||
b.Property<bool>("PhoneNumberConfirmed");
|
|
||||||
|
|
||||||
b.Property<string>("SecurityStamp");
|
|
||||||
|
|
||||||
b.Property<bool>("TwoFactorEnabled");
|
|
||||||
|
|
||||||
b.Property<string>("UserName")
|
|
||||||
.HasAnnotation("MaxLength", 256);
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NormalizedEmail")
|
|
||||||
.HasName("EmailIndex");
|
|
||||||
|
|
||||||
b.HasIndex("NormalizedUserName")
|
|
||||||
.IsUnique()
|
|
||||||
.HasName("UserNameIndex");
|
|
||||||
|
|
||||||
b.ToTable("AspNetUsers");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim<string>", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole")
|
|
||||||
.WithMany("Claims")
|
|
||||||
.HasForeignKey("RoleId")
|
.HasForeignKey("RoleId")
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
||||||
.WithMany("Claims")
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
||||||
.WithMany("Logins")
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole")
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
.WithMany("Users")
|
.WithMany()
|
||||||
.HasForeignKey("RoleId")
|
.HasForeignKey("RoleId")
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
||||||
.WithMany("Roles")
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,8 @@
|
||||||
<ProjectReference Include="..\..\..\src\UI\Microsoft.AspNetCore.Identity.UI.csproj" />
|
<ProjectReference Include="..\..\..\src\UI\Microsoft.AspNetCore.Identity.UI.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="$(MicrosoftEntityFrameworkCoreToolsDotNetPackageVersion)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
@page
|
||||||
|
@model LoginModel
|
||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
ViewData["Title"] = "Contoso log-in";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"]</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<section>
|
||||||
|
<form method="post">
|
||||||
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.Login"></label>
|
||||||
|
<input asp-for="Input.Login" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.Login" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label asp-for="Input.RememberMe">
|
||||||
|
<input asp-for="Input.RememberMe" />
|
||||||
|
@Html.DisplayNameFor(m => m.Input.RememberMe)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-default">Log in</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<partial name="_ValidationScriptsPartial" />
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Identity.DefaultUI.WebSite.Pages
|
||||||
|
{
|
||||||
|
public class LoginModel : PageModel
|
||||||
|
{
|
||||||
|
public LoginModel(IOptionsMonitor<ContosoAuthenticationOptions> options)
|
||||||
|
{
|
||||||
|
Options = options.CurrentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InputModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Login { get; set; }
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public InputModel Input { get; set; }
|
||||||
|
|
||||||
|
[BindProperty(SupportsGet = true)]
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public string State { get; set; }
|
||||||
|
|
||||||
|
public ContosoAuthenticationOptions Options { get; }
|
||||||
|
|
||||||
|
public IActionResult OnGet()
|
||||||
|
{
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnPostAsync()
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var state = JsonConvert.DeserializeObject<IDictionary<string, string>>(State);
|
||||||
|
var identity = new ClaimsIdentity(new Claim[]
|
||||||
|
{
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, Input.Login)
|
||||||
|
},
|
||||||
|
state["LoginProvider"],
|
||||||
|
ClaimTypes.NameIdentifier,
|
||||||
|
ClaimTypes.Role);
|
||||||
|
var principal = new ClaimsPrincipal(identity);
|
||||||
|
var properties = new AuthenticationProperties(state)
|
||||||
|
{
|
||||||
|
IsPersistent = Input.RememberMe
|
||||||
|
};
|
||||||
|
await HttpContext.SignInAsync(Options.SignInScheme, principal, properties);
|
||||||
|
return Redirect(ReturnUrl ?? "~/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
|
||||||
|
namespace Identity.DefaultUI.WebSite
|
||||||
|
{
|
||||||
|
public static class ContosoAuthenticationBuilderExtensions
|
||||||
|
{
|
||||||
|
public static AuthenticationBuilder AddContosoAuthentication(
|
||||||
|
this AuthenticationBuilder builder,
|
||||||
|
Action<ContosoAuthenticationOptions> configure) =>
|
||||||
|
builder.AddScheme<ContosoAuthenticationOptions, ContosoAuthenticationHandler>(
|
||||||
|
ContosoAuthenticationConstants.Scheme,
|
||||||
|
ContosoAuthenticationConstants.DisplayName,
|
||||||
|
configure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Identity.DefaultUI.WebSite
|
||||||
|
{
|
||||||
|
public static class ContosoAuthenticationConstants
|
||||||
|
{
|
||||||
|
public const string Scheme = "Contoso";
|
||||||
|
public const string DisplayName = "Contoso";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Identity.DefaultUI.WebSite
|
||||||
|
{
|
||||||
|
public class ContosoAuthenticationHandler : AuthenticationHandler<ContosoAuthenticationOptions>
|
||||||
|
{
|
||||||
|
public ContosoAuthenticationHandler(
|
||||||
|
IOptionsMonitor<ContosoAuthenticationOptions> options,
|
||||||
|
ILoggerFactory logger,
|
||||||
|
UrlEncoder encoder,
|
||||||
|
ISystemClock clock)
|
||||||
|
: base(options, logger, encoder, clock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<AuthenticateResult> HandleAuthenticateAsync() =>
|
||||||
|
Task.FromResult(AuthenticateResult.NoResult());
|
||||||
|
|
||||||
|
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
|
||||||
|
{
|
||||||
|
var uri = $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Options.RemoteLoginPath}";
|
||||||
|
uri = QueryHelpers.AddQueryString(uri, new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
["State"] = JsonConvert.SerializeObject(properties.Items),
|
||||||
|
[Options.ReturnUrlQueryParameter] = properties.RedirectUri
|
||||||
|
});
|
||||||
|
Response.Redirect(uri);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
|
||||||
|
namespace Identity.DefaultUI.WebSite
|
||||||
|
{
|
||||||
|
public class ContosoAuthenticationOptions : AuthenticationSchemeOptions
|
||||||
|
{
|
||||||
|
public ContosoAuthenticationOptions()
|
||||||
|
{
|
||||||
|
Events = new object();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SignInScheme { get; set; }
|
||||||
|
public string ReturnUrlQueryParameter { get; set; } = "returnUrl";
|
||||||
|
public string RemoteLoginPath { get; set; } = "/Contoso/Login";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-Identity.DefaultUI.WebSite-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true"
|
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-Identity.DefaultUI.WebSite-90455f3b-6c48-4aa0-a8d6-294d8e0b3d4d;Trusted_Connection=True;MultipleActiveResultSets=true"
|
||||||
|
//"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-Identity.DefaultUI.WebSite-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue