fbpx

04. PART 3 IdentityServer4 ASP.NET Core Identity .NET Core 3.1

You can find the project here.

Test data

In order to start playing with the IdentityServer4, later on, we must populate test config and user data into our database tables. Without it, we can’t start using the IdentityServer4. Changes are pretty straight forward and require adding the seed data in code and adding a setting in appsettings. When seeding is enabled the application will start, create the database, run the migrations to create both IdentityServer4 and ASP.NET Core Identity tables and populate the test data for configuration (resources and clients) and user data (users and claims).

Add test configuration and users

I will continue with the “Quickstart” solution from the previous tutorial. Open the solution and navigate to the “Data” folder. Open “IdentityDbContext.cs” and below “OnModelCreating” method create “UserSeed” method like so:

private void UsersSeed(ModelBuilder builder)
{
    var password = "My long 123$ password";

    var alice = new ApplicationUser
    {
        Id = "1",
        UserName = "alice",
        NormalizedUserName = "ALICE",
        Email = "AliceSmith@email.com",
        NormalizedEmail = "AliceSmith@email.com".ToUpper(),
        EmailConfirmed = true
    };
    alice.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(alice, password);

    var bob = new ApplicationUser
    {
        Id = "2",
        UserName = "bob",
        NormalizedUserName = "BOB",
        Email = "BobSmith@email.com",
        NormalizedEmail = "bobsmith@email.com".ToUpper(),
        EmailConfirmed = true,
    };
    bob.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(bob, password);

    builder.Entity<ApplicationUser>()
        .HasData(alice, bob);


    builder.Entity<IdentityUserClaim<string>>()
        .HasData(
            new IdentityUserClaim<string>
            {
                Id = 1,
                UserId = "1",
                ClaimType = "name",
                ClaimValue = "Alice Smith"
            },
            new IdentityUserClaim<string>
            {
                Id = 2,
                UserId = "1",
                ClaimType = "given_name",
                ClaimValue = "Alice"
            },
            new IdentityUserClaim<string>
            {
                Id = 3,
                UserId = "1",
                ClaimType = "family_name",
                ClaimValue = "Smith"
            },
            new IdentityUserClaim<string>
            {
                Id = 4,
                UserId = "1",
                ClaimType = "email",
                ClaimValue = "AliceSmith@email.com"
            },
            new IdentityUserClaim<string>
            {
                Id = 5,
                UserId = "1",
                ClaimType = "website",
                ClaimValue = "http://alice.com"
            },
            new IdentityUserClaim<string>
            {
                Id = 6,
                UserId = "2",
                ClaimType = "name",
                ClaimValue = "Bob Smith"
            },
            new IdentityUserClaim<string>
            {
                Id = 7,
                UserId = "2",
                ClaimType = "given_name",
                ClaimValue = "Bob"
            },
            new IdentityUserClaim<string>
            {
                Id = 8,
                UserId = "2",
                ClaimType = "family_name",
                ClaimValue = "Smith"
            },
            new IdentityUserClaim<string>
            {
                Id = 9,
                UserId = "2",
                ClaimType = "email",
                ClaimValue = "BobSmith@email.com"
            },
            new IdentityUserClaim<string>
            {
                Id = 10,
                UserId = "2",
                ClaimType = "website",
                ClaimValue = "http://bob.com"
            },
            new IdentityUserClaim<string>
            {
                Id = 11,
                UserId = "1",
                ClaimType = "email_verified",
                ClaimValue = true.ToString()
            },
            new IdentityUserClaim<string>
            {
                Id = 12,
                UserId = "2",
                ClaimType = "email_verified",
                ClaimValue = true.ToString()
            },
            new IdentityUserClaim<string>
            {
                Id = 13,
                UserId = "1",
                ClaimType = "address",
                ClaimValue = @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }"
            },
            new IdentityUserClaim<string>
            {
                Id = 14,
                UserId = "2",
                ClaimType = "address",
                ClaimValue = @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }"
            },
            new IdentityUserClaim<string>
            {
                Id = 15,
                UserId = "1",
                ClaimType = "location",
                ClaimValue = "somewhere"
            });
}

 

After this we need to call “UserSeed” method in “OnModelCreating” like so:

protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
//seed data
UsersSeed(builder);
}

and also we need to add migration:

Add-Migration SeedIdentityDbMigration -c IdentityDbContext -o Data/Migrations/AspNetIdentity/AspNetIdentityDb

 

For Client data seed we need to create our own class that will inherit from IdentityServer4 “ConfigurationDbContext” class. Navigate to “Data” folder and add new class called “ConfigurationDbContext” like :

using IdentityModel;
using IdentityServer4.EntityFramework.Entities;
using IdentityServer4.EntityFramework.Extensions;
using IdentityServer4.EntityFramework.Options;
using Microsoft.EntityFrameworkCore;
using System;

namespace IdentityServer.Data
{
    public class ConfigurationDbContext : IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext<ConfigurationDbContext>    
    {
        private readonly ConfigurationStoreOptions _storeOptions;

        public ConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options, ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
        {
            _storeOptions = storeOptions;
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ConfigureClientContext(_storeOptions);
            modelBuilder.ConfigureResourcesContext(_storeOptions);

            base.OnModelCreating(modelBuilder);
        }
    }
}

After this we need to register our “ConfigurationDbContext” in “Startup.cs”. Add this below “IdentityDbContext” registration :

services.AddDbContext<Data.ConfigurationDbContext>(options => options.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)));

 

Because we will use our child class “ConfigurationDbContext” instead of parent one coming from IdentityServer4 directly we need to drop the existing local database “IdentityServerQuickstart.NetCore3.1” to avoid possible table collisions. We also need to navigate to “Data/Migrations/IdentityServer” and delete “ConfigurationDb” folder.  Next step is to add new configuration migration using our “ConfigurationDbContext” class like so:

 Add-Migration InitialConfigurationMigration -c IdentityServer.Data.ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

Next step is to add “ClientSeed” method. In “ConfigurationDbContext.cs” file right below “OnModelCreating” method add new method “ClientSeed” like:

private void ClientSeed(ModelBuilder builder)
{
    builder.Entity<ApiResource>()
        .HasData(
            new ApiResource
            {
                Id = 1,
                Name = "web_api",
                DisplayName = "My Web API"
            }
        );

    builder.Entity<ApiScope>()
        .HasData(
            new ApiScope
            {
                Id = 1,
                Name = "web_api",
                DisplayName = "web_api",
                Description = null,
                Required = false,
                Emphasize = false,
                ShowInDiscoveryDocument = true,
                ApiResourceId = 1
            }
        );

    builder.Entity<IdentityResource>().HasData
        (
            new IdentityResource()
            {
                Id = 1,
                Enabled = true,
                Name = "openid",
                DisplayName = "Your user identifier",
                Description = null,
                Required = true,
                Emphasize = false,
                ShowInDiscoveryDocument = true,
                Created = DateTime.UtcNow,
                Updated = null,
                NonEditable = false
            },
            new IdentityResource()
            {
                Id = 2,
                Enabled = true,
                Name = "profile",
                DisplayName = "User profile",
                Description = "Your user profile information (first name, last name, etc.)",
                Required = false,
                Emphasize = true,
                ShowInDiscoveryDocument = true,
                Created = DateTime.UtcNow,
                Updated = null,
                NonEditable = false
            });

    builder.Entity<IdentityClaim>()
        .HasData(
            new IdentityClaim
            {
                Id = 1,
                IdentityResourceId = 1,
                Type = "sub"
            },
            new IdentityClaim
            {
                Id = 2,
                IdentityResourceId = 2,
                Type = "email"
            },
            new IdentityClaim
            {
                Id = 3,
                IdentityResourceId = 2,
                Type = "website"
            },
            new IdentityClaim
            {
                Id = 4,
                IdentityResourceId = 2,
                Type = "given_name"
            },
            new IdentityClaim
            {
                Id = 5,
                IdentityResourceId = 2,
                Type = "family_name"
            },
            new IdentityClaim
            {
                Id = 6,
                IdentityResourceId = 2,
                Type = "name"
            });

    builder.Entity<Client>()
        .HasData(
            new Client
            {
                Id = 1,
                Enabled = true,
                ClientId = "client",
                ProtocolType = "oidc",
                RequireClientSecret = true,
                RequireConsent = true,
                ClientName = null,
                Description = null,
                AllowRememberConsent = true,
                AlwaysIncludeUserClaimsInIdToken = false,
                RequirePkce = false,
                AllowAccessTokensViaBrowser = false,
                AllowOfflineAccess = false
            },
            new Client
            {
                Id = 2,
                Enabled = true,
                ClientId = "ro.client",
                ProtocolType = "oidc",
                RequireClientSecret = true,
                RequireConsent = true,
                ClientName = null,
                Description = null,
                AllowRememberConsent = true,
                AlwaysIncludeUserClaimsInIdToken = false,
                RequirePkce = false,
                AllowAccessTokensViaBrowser = false,
                AllowOfflineAccess = false
            },
            new Client
            {
                Id = 3,
                Enabled = true,
                ClientId = "mvc",
                ProtocolType = "oidc",
                RequireClientSecret = true,
                RequireConsent = true,
                ClientName = "MVC Client",
                Description = null,
                AllowRememberConsent = true,
                AlwaysIncludeUserClaimsInIdToken = false,
                RequirePkce = false,
                AllowAccessTokensViaBrowser = false,
                AllowOfflineAccess = true
            },
            new Client
            {
                Id = 4,
                Enabled = true,
                ClientId = "js",
                ProtocolType = "oidc",
                RequireClientSecret = false,
                RequireConsent = true,
                ClientName = "JavaScript client",
                Description = null,
                AllowRememberConsent = true,
                AlwaysIncludeUserClaimsInIdToken = false,
                RequirePkce = true,
                AllowAccessTokensViaBrowser = false,
                AllowOfflineAccess = false
            });

    builder.Entity<ClientGrantType>()
        .HasData(
            new ClientGrantType
            {
                Id = 1,
                GrantType = "client_credentials",
                ClientId = 1
            },
            new ClientGrantType
            {
                Id = 2,
                GrantType = "password",
                ClientId = 2
            },
            new ClientGrantType
            {
                Id = 3,
                GrantType = "hybrid",
                ClientId = 3
            },
            new ClientGrantType
            {
                Id = 4,
                GrantType = "authorization_code",
                ClientId = 4
            });

    builder.Entity<ClientScope>()
        .HasData(
            new ClientScope
            {
                Id = 1,
                Scope = "profile",
                ClientId = 3
            },
            new ClientScope
            {
                Id = 2,
                Scope = "profile",
                ClientId = 4
            },
            new ClientScope
            {
                Id = 3,
                Scope = "openid",
                ClientId = 3
            },
            new ClientScope
            {
                Id = 4,
                Scope = "openid",
                ClientId = 4
            },
            new ClientScope
            {
                Id = 5,
                Scope = "web_api",
                ClientId = 1
            }
            ,
            new ClientScope
            {
                Id = 6,
                Scope = "web_api",
                ClientId = 2
            }
            ,
            new ClientScope
            {
                Id = 7,
                Scope = "web_api",
                ClientId = 3
            }
            ,
            new ClientScope
            {
                Id = 8,
                Scope = "web_api",
                ClientId = 4
            });

    builder.Entity<ClientSecret>()
        .HasData(
                new ClientSecret
                {
                    Id = 1,
                    Value = "secret".ToSha256(),
                    Type = "SharedSecret",
                    ClientId = 1
                },
                new ClientSecret
                {
                    Id = 2,
                    Value = "secret".ToSha256(),
                    Type = "SharedSecret",
                    ClientId = 2
                },
                new ClientSecret
                {
                    Id = 3,
                    Value = "secret".ToSha256(),
                    Type = "SharedSecret",
                    ClientId = 3
                });

    builder.Entity<ClientPostLogoutRedirectUri>()
        .HasData(
        new ClientPostLogoutRedirectUri
        {
            Id = 1,
            PostLogoutRedirectUri = "http://localhost:5002/signout-callback-oidc",
            ClientId = 3
        },
        new ClientPostLogoutRedirectUri
        {
            Id = 2,
            PostLogoutRedirectUri = "http://localhost:5003/index.html",
            ClientId = 4
        });

    builder.Entity<ClientRedirectUri>()
        .HasData(
        new ClientRedirectUri
        {
            Id = 1,
            RedirectUri = "http://localhost:5002/signin-oidc",
            ClientId = 3
        },
        new ClientRedirectUri
        {
            Id = 2,
            RedirectUri = "http://localhost:5003/callback.html",
            ClientId = 4
        });

    builder.Entity<ClientCorsOrigin>()
        .HasData(
        new ClientCorsOrigin
        {
            Id = 1,
            Origin = "http://localhost:5003",
            ClientId = 4
        });
}

and call “ClientSeed” method in “OnModelCreating” :

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ConfigureClientContext(_storeOptions);
            modelBuilder.ConfigureResourcesContext(_storeOptions);

            base.OnModelCreating(modelBuilder);

            ClientSeed(modelBuilder);
        }

We need one more step to finish our client seed.

Add-Migration SeedConfigurationMigration -c IdentityServer.Data.ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

The database was dropped in previous steps so we need to re-create all tables and re-seed data for all db contexts

Update-Database -Context IdentityServer.Data.ConfigurationDbContext
Update-Database -Context PersistedGrantDbContext
Update-Database -Context IdentityDbContext

Run the application to log in

Run the IdentityServer4 and navigate to http://localhost:5000/Account/Login

You should be able to login now with user “alice” and password “My long 123$ password”. After login, you should be redirected back to the IdentityServer4 home page with the name “Alice Smith” displayed in the upper left corner.

Explore the database

I encourage you to take a look at the “IdentityServerQuickstart.NetCore3.1” database. You will see it is re-created will all the tables, migrations and data. Explore the data in tables and compare it to data in “Users.cs” and “Config.cs” in the “Data/Seed” folder to learn how and where the data is stored. In the next tutorial we will take a look at the custom properties we can add to extend the “User” model.

You can find the project here.

Support

For direct assistance schedule a technical meeting with Ivan to talk about your requirements.
For a general overview of our services and a live demo schedule a meeting with Maja.

Comments
  • Raju Nadimpalli says:

    Hi, Thank you for a great tutorial, i am getting an issue when i run below migration command
    Add-Migration InitialConfigurationMigration -c IdentityServer.Data.ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

    Unable to create an object of type ‘ConfigurationDbContext’. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
    please help me

    • deblokt says:

      Hi,

      We will update the tutorial to fix this problem today. Please add to Startup.cs this line after the first “services.AddDbContext”:
      “services.AddDbContext(options => options.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)));”

  • Theunis van der Stoep says:

    Just as a side not you might want to mention to add this to Startup.cs

    services.AddDbContext(options =>
    options.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly))
    );

    Otherwise it will not run the migration

    • Theunis van der Stoep says:

      Another thing I have also noticed is that you have to drop the entire db and run the migrations again, as the configuration migrations are conflicting on table names that already existed and they were never first removed before the you mention to delete the configuration folder.

      • deblokt says:

        Hi. You are right again. Seems like we need to revisit this particular tutorial and add all updates. Thank you.

    • deblokt says:

      Hi,
      Thank you. You are right, we will update the tutorial.

  • Serg says:

    Hi,
    I am getting this error on successful login:
    SqlException: Invalid object name ‘PersistedGrants’.
    It is coming from IdentityServer.GrantsController.BuildViewModelAsync() in GrantsController.cs:
    var grants = await _interaction.GetAllUserConsentsAsync();
    Also, not sure if Users.cs referred to in the last section exists since update.

    • deblokt says:

      Hi Serg,

      Please make sure of a couple of things:

      1. Drop your Identity database (delete it)
      2. Make sure your connecting string is valid in appsettings
      3. Run Update-Database for all contexts (IdentityDbContext, PersistedGrantDbContext and IdentityServer.Data.ConfigurationDbContext)
  • Andreas says:

    I really don’t understand this last part, before this section this was a great tutorial. This feels rushed and untested.
    When I try to migrate I keep getting errors like “More than one DbContext named ‘ConfigurationDbContext’ was found” or “No DbContext named ‘ConfigurationDbContext’ was found”. Does this really work? Other than this last part, this has been a great tutorial.

    • deblokt says:

      Thank you for your feedback. We actually take great care to explain each step properly. Upon your feedback we updated this tutorial to make it easier to follow and understand. Please check it out. ConfigurationDbContext changes are now better outlined in the tutorial as I see that is where your confusion is. Thanks.

  • Katie Schuler says:

    Question – After finishing this tutorial my database has a few groups of tables that are nearly identical. One example, I have a table called dbo.IdentityResource and another called dbo.IdentityResources. They have the same columns and both have records for openid and profile. The only difference I’m finding (besides one table being named plural and one not) is that dbo.IdentityResource has another record for email. I had dropped all tables in the database before recreating it again near the end of the database. Is this how it is supposed to turn out?

    • deblokt says:

      Hi Katie,

      There was a minor issue with the “UsersSeed” migration. We updated the tutorial to fix it.

      Because you already created the migrations and updated the database there are a couple of extra steps you need to perform. Please drop the database, manually remove all migrations (delete “Migrations” folder), add all migrations using “Add-Migration” and run “Update-database” for all 3 db contexts. This will re-create the migrations and database properly. Sorry for the confusion and thank you for reporting.

  • Karen Bench says:

    All tutorials up to this point have been pretty good.
    I had to edit about all the commands to replace -o with -OutputDir and then they have mostly worked.

    ON this step.
    How do I accomplisth
    “Because we will use our child class “ConfigurationDbContext” instead of parent one coming from IdentityServer4 directly we need to drop the existing local database “IdentityServerQuickstart.NetCore3.1” to avoid possible table collisions. We also need to navigate to “Data/Migrations/IdentityServer” and delete “ConfigurationDb” folder.”
    I tried several things, but I still receive.
    SQLite Error 1: ‘table “ApiResources” already exists’.
    when I issue the Update-database -c IdentityServer.Data.ConfiguratinoDbContext.

  • Karen Bench says:

    I tried Drop-database IdentityServerQuickstart.NetCore3.1. nope
    I tried Drop-database -c IdentityDbContext
    I tried drop-database -c IdentityServer.Data.ConfigurationDbContext
    I tried drop-database -c IdentityServer.Data.ConfigurationDbContext
    I get the following error:
    The process cannot access the file ‘c:\sqlite\UsersDB.db’ because it is being used by another process.

    PM> add-migration SeedIdentityDbMigration -c IdentityDbContext -OutputDir Data/migrations/AspNetIdentity/AspNetIdentityDb
    Build started…
    Build succeeded.
    The name ‘SeedIdentityDbMigration’ is used by an existing migration.

  • Karen Bench says:

    I also tried
    Remove-migration -c each of 3 contexts
    ok
    Add-Migration again for all 3 contexts
    ok
    Update-Database -c IdentityServer.Data.ConfigurationDbContext
    says “Error 1: ‘table “ApiResources” already exists’.
    Update-Database -c PersistedGrantDbContext
    Update-Databae -c IdentityDbContext
    says AspNetRoles already exists.
    No data loads into the database.

  • Michael Drexler says:

    Hy Deblokt,

    I have to say that this blog aboubt IdentityServer4 + Asp.Net Core Identity is exactly what I was looking for.
    This is a really great blog and guid+explanation of this really huge and complex topic. Thank you very much.

    I still have 2 questions:
    – Is there an AdminUI provided from IdentityServer4 to manage all the clients, scopes, etc… via UI?
    – Is there a big effort for production to switch to HTTPS or just the little change in the launchSettings and getting a certificate?

  • Robin says:

    Hi DeBlokt,

    Thanks for this amazing tutorial. I managed to get everything up and running.
    I only got 1 problem and I got stuck with it for almost a week already.

    When I’m building an api and I want to validate the token I always get the next message:
    Bearer error=”invalid_token”, error_description=”The signature key was not found”
    Only when I remove the Authority from .AddJwtBearer I got a successful response, but then the token isn’t validated by the IssuerSigningKey.
    I read something like “Probably your token may not have a private key signature” but I have no idea how to add it. Do you have any clue?
    Thanks in advance

  • Evren says:

    Hi,
    Great tutorial but im unable to access /Account/Register endpoints (I guess this are handling by Identity). Manage etc. endpoints are not working too.

    Best,

    • deblokt says:

      Hi Evren. Use our example on GitHub (you can get the link at the top of every tutorial) and compare it with your code. Thanks

      • Juan says:

        Hi Deblokt,
        Great tutorial!
        I’m having the same issue as Evren, I tried downloading the tutorial and also got the same issue.
        *Could this be happening because the endpoint needs to be declared similar to login page? (options.UserInteraction.LoginUrl = “/Account/Login”;)
        *Could this be related to Microsoft having move WindowsPrincipal, WindowsIdentity to new package?(System.Security.Principal.Windows needs to be added to the project to compile)
        Best Regards

    • deblokt says:

      Please head over to tutorial http://debloktc.wwwnl1-ts3.a2hosted.com/2020/01/24/07-identityserver4-mfa-totp-net-core-3-1/ where we start adding MFA and take a look at Startup to find the razor pages registration. It should have been done sooner but we don’t use the Identity pages until that point in the tutorial and that is where we set it up. Correct URL is “/identity/account/register”.

  • Lin Qiao says:

    Hi,
    This tutorial is the best Identity Server 4 material I’ve ever seen.
    Some small issues occured to me though.
    I tried 3 browsers:
    1- Computer A: Chrome (76.0.3809.100) (x86)
    2-Computer B: Chrome (80.0.3987.162) (x64)
    3- Computer B: Edge (44.17763.831.0)
    Alice can not login on 2-Chrome 80, and I found that the cookies did not contain the Asp.NetCore.Identity value.
    Do you have some suggestions?

    • deblokt says:

      Please run localhost in HTTPS and it will work. Chrome is dropping samesite:none cookies from HTTP need to use HTTPS.

  • Juan says:

    Hi Deblokt, nice tutorial,
    I’m having same issue with the GitHub version
    Regards

  • Sam Khanjar says:

    Hi,
    Does anyone know how I can request for refresh_token? I’m trying to get refresh_token when requesting for access_token. At the moment I’m getting this output.
    “{
    “access_token”: “eyJhbGciOiJSU…Zp1eS1WLY2KW2cLvEHhfDfikAYhPEDbAHTvtQu_yBgRsRxhTTPA”,
    “refresh_token”: null,
    “expires_in”: “3600”,
    “token_type”: “Bearer”,
    “scope”: “app.api.whatever.full”
    }”
    Hence refresh_token is null. Now I know you can’t have refresh token with grant type of client credentials. Therefore I have created another two users one with grant type of password and other of grant type of hybrid. I also set the scope for both to offline access. When requesting for a token I get invalid_client. I’m stuck and I have no idea how to resolve this issue.
    This is how I’m requesting for token:
    public async Task GetUserToken(string client_id, string client_secret, string username, string password, string scope)
    {
    var client = new HttpClient();

    var dict = new Dictionary();
    dict.Add(“client_id”, client_id);
    dict.Add(“client_secret”, client_secret);
    dict.Add(“grant_type”, “password”);
    dict.Add(“scope”, scope);
    dict.Add(“username”, username);
    dict.Add(“password”, password);

    client.BaseAddress = new Uri($”{Request.Scheme}://{Request.Host.Value}”);
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));

    var req = new HttpRequestMessage(HttpMethod.Post, “/connect/token”) { Content = new FormUrlEncodedContent(dict) };
    var res = await client.SendAsync(req).ConfigureAwait(false);

    if (res.IsSuccessStatusCode)
    {
    return Ok(res.Content.ReadAsStringAsync().Result);
    }
    return BadRequest();
    }

  • Sy says:

    Hi Deblokt.
    I try to run the Migration: ” Update-Database -Context IdentityServer.Data.ConfigurationDbContext ” there is error ” There is already an object named ‘ApiResources’ in the database. ” and the same with ” Update-Database -Context IdentityDbContext ” -> ” There is already an object named ‘AspNetRoles’ in the database “. I can’t drop that table because it’s foreign key.

    I ignore that error and running project, It’s logging fail with message ” Invalid username or password “.

    Any sugguestion?

    Thanks.

  • Aiden says:

    Hi Deblokt,
    Can we reuse Client Seed from method InitializeDatabase in older post (http://debloktc.wwwnl1-ts3.a2hosted.com/2019/09/26/04-part-3-identityserver4-asp-net-core-identity/) and combine with method for User Seed in this post?
    Thanks

    • deblokt says:

      Hi. The seed methods are a bit different in 2.x and 3.x .net core versions. You can change it however you see fit but the old way will not work with .net core 3.x.

  • Serjoka says:

    Thank you a lot for your great tutorial!
    After running the last migration script
    Update-Database -Context PersistedGrantDbContext

    I get the following error:
    There is already an object named ‘AspNetRoles’ in the database.

  • hema says:

    Update-Database -Context IdentityServer.Data.ConfigurationDbContext
    while doing the above comment im getting this following error

    System.InvalidOperationException: To change the IDENTITY property of a column, the column needs to be dropped and recreated.
    at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
    at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.c.b__71_4(MigrationsSqlGenerator g, MigrationOperation o, IModel m, MigrationCommandListBuilder b)
    at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
    at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
    at Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)
    at Microsoft.EntityFrameworkCore.Migrations.SqlServerMigrationsSqlGenerator.Generate(IReadOnlyList`1 operations, IModel model)
    at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration)
    at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.c__DisplayClass15_2.b__2()
    at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
    at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
    at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
    at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.c__DisplayClass0_0.b__0()
    at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
    To change the IDENTITY property of a column, the column needs to be dropped and recreated.

  • Werner Kellens says:

    First of all: great tutorials!

    I noticed one thing with the migrations. The first seed migrations works good but if I add a migration immediately afterwards I get an update for the Created property because of this ‘DateTime.UtcNow’. Every add of a migration this value is different. So I have set the Created date to new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day) so that I don’t have a conflict anymore.

  • SreeRam says:

    Hi Deblokt,

    Appreciated for your efforts to keep this tutorial up to date. I see this tutorial and the code samples are based on IdentityServer4 v3.1.4 and it looks like there are breaking changes in .net core 3.1 and IdentityServer4 v4.0 or later. Initially I tried using this with latest versions but hit with many issues later I tried with the same versions as your samples using and worked fine. Especially client seeding part (ClientSeed method in ConfigurationDbContext.cs) doesn’t work as there are breaking changes in IdentityServer4.EntityFramework.Entities. In case you get a chance to look into it please update once again as the users like me mostly try to use the latest versions.

    Regards,
    SreeRam

    • spohl says:

      +1

      • deblokt says:

        We currently don’t have available resources to update the tutorials. If you would like to contribute by creating the tutorials for the latest ID4 version please get in touch and we can publish your post or add a link to your blog post. Thank you.

  • FATMI Adil says:

    Hi, i tried to access account/login url but i got 404 error.

  • Paudel says:

    Hello there, I dropped all the database and updated again but encountered an error while logging in. This chapter of the tutorial seems to be confusing, otherwise is a great tutorial. Thanks a lot for your effort.

    An unhandled exception occurred while processing the request.
    SqlException: Invalid object name ‘AspNetUsers’.

    Microsoft.Data.SqlClient.SqlCommand+c.b__164_0(Task result)

    SqlException: Invalid object name ‘AspNetUsers’.
    Microsoft.Data.SqlClient.SqlCommand+c.b__164_0(Task result)
    System.Threading.Tasks.ContinuationResultTaskFromResultTask.InnerInvoke()
    System.Threading.Tasks.Task+c.b__274_0(object obj)
    System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, object state)
    System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, object state)
    System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref Task currentTaskSlot, Thread threadPoolThread)
    Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable+AsyncEnumerator.InitializeReaderAsync(DbContext _, bool result, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync(TState state, Func<DbContext, TState, CancellationToken, Task> operation, Func<DbContext, TState, CancellationToken, Task<ExecutionResult>> verifySucceeded, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable+AsyncEnumerator.MoveNextAsync()
    System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()
    Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync(IAsyncEnumerable asyncEnumerable, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync(IAsyncEnumerable asyncEnumerable, CancellationToken cancellationToken)
    Microsoft.AspNetCore.Identity.UserManager.FindByNameAsync(string userName)
    Microsoft.AspNetCore.Identity.SignInManager.PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure)
    IdentityServer.AccountController.Login(LoginInputModel model, string button) in AccountController.cs

    var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: true);

    Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor+TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, object controller, object[] arguments)
    System.Threading.Tasks.ValueTask.get_Result()
    System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()
    Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask actionResultValueTask)
    Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
    Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
    Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
    Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
    Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
    IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events)
    IdentityServer4.Hosting.MutualTlsTokenEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes)
    Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
    IdentityServer4.Hosting.BaseUrlMiddleware.Invoke(HttpContext context)
    Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

    Thanks a lot in advance.

Comments are closed.