fbpx

01. IdentityServer4 Quickstart .NET Core 3.1

You can find the project here.

Setup the project and run it in Visual Studio

In order to make our lives a bit easier, the team responsible for IdentityServer4 created dotnet templates to quickstart various ID4 projects. We will start with a quickstart template (including basic UI) to get to know the basic parts of the ID4 first. Let’s follow their command-line instructions so we have the same project structure but I will go more into details of each part of the project. You can find the official quickstart tutorial here but it’s not necessary to have multiple tabs open as I will explain it all here. First, open up the command line and type (or paste) these commands:

 

dotnet new -i IdentityServer4.Templates
md quickstart
cd quickstart
md src
cd src
dotnet new is4empty -n IdentityServer
dotnet new is4ui -n IdentityServer
cd ..
dotnet new sln -n Quickstart
dotnet sln add .\src\IdentityServer\IdentityServer.csproj

 

These commands install the needed dotnet templates for ID4. Then it creates a “quickstart” folder and “src” folder inside it, IdentityServer4 project with basic UI in the “src” folder, “Quickstart” solution file and adds IdentityServer4 project to the solution file. Let’s now navigate to the “quickstart” folder and open the “Quickstart” solution in Visual Studio.

The next step is to build the solution to verify it builds successfully. This step will also download all needed NuGet packages. There are three places in “Startup.cs” file that we need to uncomment in order to get the basic UI working. IdentityServer4 uses MVC with razor views for basic UI which can be customized as needed to create beautiful pages. Run the application and you should see browser opening up on localhost port 5000 with IdentityServer4 welcome page. Congratulations you just successfully started your first IdentityServer4 instance!

 

 

Nuts and bolts

Let’s take a look at the code for a bit to get a sense of the beast. Bring up Solution Explorer in Visual Studio and you should see the structure below.

 

 

Here we see one solution “Quickstart” with a single project in it called “IdentityServer”. This project is (at the time of writing) by template default a .Net Core 2.1 application. We have “wwwroot” folder which contains all static resources (CSS, js, images). “Quickstart” folder is more interesting as it contains the MVC controllers which control the actions for all pages. Most frequently used are “AccountController.cs” under “Account” subfolder and “HomeController.cs” under “Home” subfolder. We will go through the AccountController in more details below. HomeController contains the default “Index” action responsible for displaying the index page (the welcome page we saw a minute ago). There are other subfolders containing controllers, options, and models for other functionalities like consent, diagnostics, grants, etc.

“Views” folder contains razor views for actions defined in the controllers. Controller actions control the behavior and views make up the visual appearance. In the root folder, we have a “Startup.cs” file that we modified earlier to uncomment lines needed for MVC. “Program.cs” bootstraps the .Net Core application telling it we need to create a web application (with Kestrel by default) and configures the logging. Notice the “identityserver4_log.txt” which is the file automatically created after you run the app for the first time. It contains the runtime logs that you can easily view while running in localhost. The “tempkey.rsa” is also automatically generated to sign the JWT tokens used for OAuth2 and OpenID Connect. We will replace this automatically generated keys in one of the next tutorials to get the application production-ready.

For development, the temporary key is just fine. Root folder also contains “Config.cs” which is currently more-less empty but it can be used to provide in-memory resource and client configuration.

Now let’s get back to the “AccountController.cs”. Most of the changes to the login flow are going to be facilitated here as this controller holds the login and logout action methods. “ExternalController.cs” is kind of the same story but for the external login. Local login is based on the local user store and external login is based on another Identity Provider (think Google for example) and the user logs in into an external user store and is linked back to the local user store. This will also be explained in one of the future tutorials.

 

When template is created open Program.cs in Main method add into Logger definition to create log file

.WriteTo.File(@"identityserver4_log.txt")

 

In-memory vs database storage

By default, the IdentityServer4 template configures the in-memory storage for configuration store (client store, api and identity resource store, CORS policy store), operational store (persisted grants store for tokens, codes and consents) and user store. This is fine to get your feet in the water and test it out with your existing applications. It runs fast, it’s easy to change the config and there is no need to provide the SQL instance of any database.

For the production environment, however, it is not recommended to run the in-memory store as it requires the IdentityServer4 application to be re-published each time you want to change something (add client, add user, change the user password, add user claim, etc) which is not practical. It also limits who can do those changes as the developer is required to do changes in code and re-publish. Also, it can cause operational data loss if the application gets recycled. The next tutorial will explain the use of the code-first Entity Framework to migrate all of this data to the MSSQL database. For those who want to keep it all open-source (and free), I will also create a tutorial on how to migrate and use PostgreSQL instead of the MSSQL.

 

Endpoints

Let’s run the IdentityServer4 again and take a look at the interesting endpoints. The welcome page already gives us a clue to it. It is called discovery document endpoint. If you are following the tutorial and running application on localhost port 5000 you should be able to access it here http://localhost:5000/.well-known/openid-configuration. I will format this JSON a bit so it looks nicer for us here:

 

{
   "issuer":"http://localhost:5000",
   "jwks_uri":"http://localhost:5000/.well-known/openid-configuration/jwks",
   "authorization_endpoint":"http://localhost:5000/connect/authorize",
   "token_endpoint":"http://localhost:5000/connect/token",
   "userinfo_endpoint":"http://localhost:5000/connect/userinfo",
   "end_session_endpoint":"http://localhost:5000/connect/endsession",
   "check_session_iframe":"http://localhost:5000/connect/checksession",
   "revocation_endpoint":"http://localhost:5000/connect/revocation",
   "introspection_endpoint":"http://localhost:5000/connect/introspect",
   "device_authorization_endpoint":"http://localhost:5000/connect/deviceauthorization",
   "frontchannel_logout_supported":true,
   "frontchannel_logout_session_supported":true,
   "backchannel_logout_supported":true,
   "backchannel_logout_session_supported":true,
   "scopes_supported":[
      "openid",
      "offline_access"
   ],
   "claims_supported":[
      "sub"
   ],
   "grant_types_supported":[
      "authorization_code",
      "client_credentials",
      "refresh_token",
      "implicit",
      "urn:ietf:params:oauth:grant-type:device_code"
   ],
   "response_types_supported":[
      "code",
      "token",
      "id_token",
      "id_token token",
      "code id_token",
      "code token",
      "code id_token token"
   ],
   "response_modes_supported":[
      "form_post",
      "query",
      "fragment"
   ],
   "token_endpoint_auth_methods_supported":[
      "client_secret_basic",
      "client_secret_post"
   ],
   "subject_types_supported":[
      "public"
   ],
   "id_token_signing_alg_values_supported":[
      "RS256"
   ],
   "code_challenge_methods_supported":[
      "plain",
      "S256"
   ]
}

 

Discovery document is useful to clients using IdentityServer4 as their Identity Provider. It provides all endpoints of interest (authorization endpoint, token endpoint, etc), supported scopes, claims, grant types, response types, response modes, auth methods, token signing algorithms, PKCE code challenge methods.

This saves time as instead of manually providing these endpoints we can just provide the discovery document aka metadata endpoint. It is also useful to developers trying to figure out how to manually invoke endpoints to get the token as it provides the endpoint URLs and supported grant types to know which flows to use.

Another endpoint of high interest is, of course, the login page itself which can be accessed here http://localhost:5000/account/login. We are using HTTP which is a big no-no when entering user credentials as it will be sent to the server in clear text but remember we are in localhost using hard-coded in-memory users at the moment. To get the IdentityServer4 production-ready we will need to make sure we use HTTPS only. This will also be explained in one of the future tutorials along with the certificate implementation for production. Ok, enough chitchat lets login. Type in “alice” as username and password (same values for both, both lowercase, without quotes) and click “Login”.

 

If all goes well you will be redirected back from where you came from. In our case the welcome page as we navigated to the login page manually but in the real world it would be the home page of the client application, not the IdentityServer4 itself. Notice that you are now logged in as user “alice”.

 

Congratulations!

You managed to create the project, build it, start it, learn something about it and log in. But wait. How did I know that username is “alice” and how did I know the password for the user? It wasn’t specified in the “Config.cs” file. You can actually see the in-memory users when you go to Solution Explorer in Visual Studio navigate to “Quickstart” folder and open file “TestUsers.cs”. It defines a list of test users and by default, there are two users, “alice” and “bob”. You can also see the claims set to those users.

 

public static List<TestUser> Users = new List<TestUser>
{
    new TestUser{SubjectId = "818727", Username = "alice", Password = "alice", 
        Claims = 
        {
            new Claim(JwtClaimTypes.Name, "Alice Smith"),
            new Claim(JwtClaimTypes.GivenName, "Alice"),
            new Claim(JwtClaimTypes.FamilyName, "Smith"),
            new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"),
            new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
            new Claim(JwtClaimTypes.WebSite, "http://alice.com"),
            new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json)
        }
    },
    new TestUser{SubjectId = "88421113", Username = "bob", Password = "bob", 
        Claims = 
        {
            new Claim(JwtClaimTypes.Name, "Bob Smith"),
            new Claim(JwtClaimTypes.GivenName, "Bob"),
            new Claim(JwtClaimTypes.FamilyName, "Smith"),
            new Claim(JwtClaimTypes.Email, "BobSmith@email.com"),
            new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
            new Claim(JwtClaimTypes.WebSite, "http://bob.com"),
            new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json),
            new Claim("location", "somewhere")
        }
    }
};

 

Another interesting endpoint that can help you figure out what is going on is the diagnostic endpoint which can be accessed in localhost only http://localhost:5000/diagnostics. This endpoint will provide you information from the authentication cookie for the currently logged in user.

 

“sub” claim is called subject claim and is the unique identifier for the user. “auth_time” is in UNIX time format. “amr” claim has value “pwd” which means we logged in using local login (with username and password, not using an external provider for example). Other claims are self-explanatory.
Lets logout. We can do that by going here http://localhost:5000/account/logout

 

 

Stay hungry, stay foolish

I strongly advise you to get more familiar with nuts and bolts and endpoints before attempting the next tutorials. Explore, write down where the important stuff is. Brake the code, make it work again, modify it, observe the results. Look at the log file for more information on what went wrong. These tutorials are more concerned about us exploring and learning IdentityServer4 itself then connecting it to something else. I will have tutorials on how to use IdentityServer4 with JS or Web API clients but that is not the focus of these tutorials.

Almost all tutorials online give you just that: how to connect with different clients without explaining the IdentityServer4 itself. I really love the IdentityServer4 project and I think it deserves more time. It is a tool we use to get to the end goal but over the years it became one of my favorite tools.

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
  • Toby says:

    Hi, Thanks for this wonderful article of identityserver4. This is the best article I’ve read so far. The explanation is very comprehensive. But I think there might have been some Typo error, while running the installation command. The command ” dotnet add new sln” has a wrong path to Identityserver.csproj. It is missing a slash( / ) I guess. Thanks once again.

  • Martin says:

    Thank you so much to Deblotk team for these tutorials!. They’ve been really helpful for me and my team. I’m currently developing a SPA with Vue.js / Vuetify with a WebApi in NetCore 3.1, right now, I’m trying to setup the user system with Identity Server. I was a bit lost on IS4 configuration. I’m looking forward for tutorias on JS and WebApi clients.

    I see that on most tutorials with SPA/WebApi and IdentityServer likes to have all projects in the same Visual Studio solution, but I’d like to keep mine separate. I work my SPA in VSCode and my WebApi and IdentityServer in visual studio in separate solutions, I think that shouldn’t be a problem right?

    • deblokt says:

      Hi Martin. IdentityServer4 is using OAuth2 and OpenId Connect. As long as you use compatible middleware or JS clients in your apps, you are good to go. It doesn’t matter where the IdentityServer4 project is or where it is deployed. It is deployed as AaaS (Authentication as a Service) anyway.

  • marina says:

    thank you for your very great tuto,
    can you please add register fonctionality in upcomming tuto

  • KC says:

    thank you for the tutorials. I am using identity server 4 with .net framework. In my application every 5 mins I am redirected towards the identity server url for short period of time say and every 30 mins I am redirected to the login page even I am actively using the application. I am looking for solution on the internet but couldn’t find one. Could you help me.

  • Kadir Kılıçoğlu says:

    Hi, this is a wonderful series about IS4 and I deeply appreciate your effort, well done!

    With recent updates, there will be an issue on Chrome. Currently, I am using Chrome-80 and it will not log in properly.

    For instance; if I click on Grants, I am redirected to the Login Page as expected but then even if I input the correct credentials, it will keep staying on the same Login Page. Though I see on logs that my user has successfully signed in.

    Then I applied some changes to honor the SameSite policy of Chrome and enable HSTS, it started to work fine.
    It’s been some time since you created this tutorial but you may consider updating it for future followers.

    Additionally, one minor issue requires to add “using IdentityServer4;” in AccountController and ExternalController as the IdentityServerUser requires that reference. Otherwise, anyone will get a build error. It is easy to fix it but worth to mention if you are going to update the tutorial!

    • deblokt says:

      Chrome changes shouldn’t affect ID4 as it’s setting SameSite none by default. It could affect it in localhost if coming from HTTP. Thanks

  • prageeth says:

    Hi,
    Please make sure enabling mvc in startup.cs configuration (it disabled by default.) otherwise it wont show the login page.
    Thanks.

    • deblokt says:

      Yes this is mentioned in our tutorial, you can also take a look at the GitHub repo for this particular tutorial. Thanks

  • Leonard E. James says:

    I just completed the setup for the initial build of the Quickstart solution. I got the following errors on the build:
    1>—— Rebuild All started: Project: IdentityServer, Configuration: Debug Any CPU ——
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\AccountController.cs(130,38,130,56): error CS0246: The type or namespace name ‘IdentityServerUser’ could not be found (are you missing a using directive or an assembly reference?)
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(123,30,123,48): error CS0246: The type or namespace name ‘IdentityServerUser’ could not be found (are you missing a using directive or an assembly reference?)
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(159,38,159,54): error CS1069: The type name ‘WindowsPrincipal’ could not be found in the namespace ‘System.Security.Principal’. This type has been forwarded to assembly ‘System.Security.Principal.Windows, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ Consider adding a reference to that assembly.
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(175,39,175,60): error CS1503: Argument 1: cannot convert from ‘string’ to ‘System.IO.BinaryReader’
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(176,39,176,57): error CS1503: Argument 1: cannot convert from ‘string’ to ‘System.IO.BinaryReader’
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(181,45,181,60): error CS1069: The type name ‘WindowsIdentity’ could not be found in the namespace ‘System.Security.Principal’. This type has been forwarded to assembly ‘System.Security.Principal.Windows, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ Consider adding a reference to that assembly.
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(182,61,182,70): error CS1069: The type name ‘NTAccount’ could not be found in the namespace ‘System.Security.Principal’. This type has been forwarded to assembly ‘System.Security.Principal.Windows, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ Consider adding a reference to that assembly.
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(183,62,183,80): error CS1503: Argument 1: cannot convert from ‘string’ to ‘System.IO.BinaryReader’
    1>Done building project “IdentityServer.csproj” — FAILED.
    ========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

    I tried the build from the GitHub repository and got:
    1>—— Rebuild All started: Project: IdentityServer, Configuration: Debug Any CPU ——
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\AccountController.cs(130,38,130,56): error CS0246: The type or namespace name ‘IdentityServerUser’ could not be found (are you missing a using directive or an assembly reference?)
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(123,30,123,48): error CS0246: The type or namespace name ‘IdentityServerUser’ could not be found (are you missing a using directive or an assembly reference?)
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(159,38,159,54): error CS1069: The type name ‘WindowsPrincipal’ could not be found in the namespace ‘System.Security.Principal’. This type has been forwarded to assembly ‘System.Security.Principal.Windows, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ Consider adding a reference to that assembly.
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(175,39,175,60): error CS1503: Argument 1: cannot convert from ‘string’ to ‘System.IO.BinaryReader’
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(176,39,176,57): error CS1503: Argument 1: cannot convert from ‘string’ to ‘System.IO.BinaryReader’
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(181,45,181,60): error CS1069: The type name ‘WindowsIdentity’ could not be found in the namespace ‘System.Security.Principal’. This type has been forwarded to assembly ‘System.Security.Principal.Windows, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ Consider adding a reference to that assembly.
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(182,61,182,70): error CS1069: The type name ‘NTAccount’ could not be found in the namespace ‘System.Security.Principal’. This type has been forwarded to assembly ‘System.Security.Principal.Windows, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ Consider adding a reference to that assembly.
    1>L:\Training\Deblokt\Quickstart\LEJ-Src\IdentityServer\Quickstart\Account\ExternalController.cs(183,62,183,80): error CS1503: Argument 1: cannot convert from ‘string’ to ‘System.IO.BinaryReader’
    1>Done building project “IdentityServer.csproj” — FAILED.
    ========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

    I searched the files and could not find the file, IdentityServerUser.cs.

    • deblokt says:

      It could be that the ID4 template has issues for some reason. You can always use our GitHub repo for this tutorial and compare your code. You can find the link at the top. Thanks

      • Leonard E. James says:

        I found the issues, there are two:
        1. From a discussion in the Pluralsight course, Securing AAP.NET Core 3 with OAuth2 and OpenID Connect, I added the NuGet package, System.Security.Principal.Windows v. 4.7.0. This reduced errors to two.
        2. There are references in AccountController and ExternalController to IdentityServerUser that are unresolved. Adding the statement, “using IdentityServer4;”, to these files resolved these errors.

        I am using .NET Core 3.1.

        • David H says:

          Yep. Not sure why the templates have not been fixed yet as of today.

          1. Add nuget System.Security.Principal.Windows
          2. using IdentityServer4;”

          • Mukti says:

            Hi
            I followed step 1 and 2, the errors are gone but localhost port 5000 does not show up the welcome screen. Do we need to do something else than this?

    • Mario Levesque says:

      add “using IdentityServer4;” in AccountController and ExternalController as the IdentityServerUser requires that reference.

  • Mario Levesque says:

    I changed the Quickstart directory name to Controllers for the endpoints.MapDefaultControllerRoute(); in Statup.cs to work and reach the login page.

  • Zeo says:

    Just used this tutorial to set-up a starting point.

    The type or namespace name ‘IdentityServerUser’ could not be found (are you missing a using directive or an assembly reference?)

    @AccountController.cs:130 and @ExternalController.cs:123

    Looks like both files are missing the using of IdentityServer4.

  • Aerrol says:

    after clicking Publish, the Quickstart and Views folder are not present in the publish folder, how to fix this?

Comments are closed.