I have an Identity Server 4 solution with EF Identity DB. I can login with my email and external gmail account, but when I try to login using OpenID (User name and Password) I receive the error below. The issue maybe with the info stored in the Identity DB tables. I'm new to Identity Server and this is my first attempt working with EF Identity DB. I can post DB info if it helps resolve the issue.
Source code: https://github.com/gotnetdude/GotNetDude-PublicRepository/tree/master/AuthServer
Identity Server Log File: https://github.com/gotnetdude/GotNetDude-PublicRepository/blob/master/AuthServer_log.txt
MVC Client Log: https://github.com/gotnetdude/GotNetDude-PublicRepository/blob/master/MVCClient_log.txt
Here is the AuthServer Startup code where I add oidc mvc client as the challenge option ("OpenID Connect") which is fail. The MVC client works fine if I login with the email credentials. I guess is that this has some to do with the way the scope is being handled on the mvc client. Any suggestion are appreciated.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using AuthServer.Data;
using AuthServer.Models;
using AuthServer.Services;
using System.Reflection;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.Logging;
namespace AuthServer
{
public class Startup
{
#region "Startup"
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
#endregion
#region "ConfigureServices"
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc();
string connectionString = Configuration.GetConnectionString("DefaultConnection");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddAspNetIdentity<ApplicationUser>()
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 15; // interval in seconds. 15 seconds useful for debugging
});
services.AddAuthentication()
.AddGoogle("Google", options =>
{
options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
})
//.AddOpenIdConnect("oidc", "OpenID Connect", options =>
//{
// //options.Authority = "https://demo.identityserver.io/";
// //options.ClientId = "implicit";
// //options.SaveTokens = true;
.AddOpenIdConnect("oidc", "OpenID Connect", options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.ClientId = "mvc";
//options.Scope.Add("api1.APIScope");
//options.Scope.Add("api1.IdentityScope");
//options.Scope.Add("openid");
//options.GetClaimsFromUserInfoEndpoint = true;
//options.Scope.Add("email");
//options.Scope.Add("profile");
//options.Scope.Add("offline_access");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
}
#endregion
#region "Configure"
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
// app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware
app.UseIdentityServer();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
#endregion
}
}
After working with the AccountService, BuildLoginViewModelAsync method for awhile, I came to the realization that the email login and the user id login are both using OpenId. I decided that rather than challenging with another OpenId for the user id that I would update the account controller sign in manager passwordsigninasync method:
//var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, lockoutOnFailure: false);
I also updated the login view:
<div class="form-group">
@*<label asp-for="Email"></label>
<input asp-for="User" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>*@
<label asp-for="Username"></label>
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
I also updated the InputViewModel:
public class LoginViewModel
{
[Required]
//[EmailAddress]
//public string Email { get; set; }
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
//public object Username { get; internal set; }
public object RememberLogin { get; internal set; }
public string Email { get; internal set; }
}
Lastly I removed the OpenID Connect challenge for the Authority startup class.
After making the above listed changes, I'm able to login with the EF Identity DB Username rather than the email using OpenID. For my purposes this is good enough of a solution. I appreciate all the contributions feel free to leave me any comments. Paul