fbpx

01. IdentityServer4 Quickstart

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.

 

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
  • Søren says:

    I really like the thoroughness of this article series, but could it possibly be rewritten to refrain from using scaffolding?
    As smart as it is, when it’s working (which it very often isn’t) it leaves the reader completely puzzled with no idea what the twenty newly added files really do.
    For the repetitive task for the skilled programmer, it’s probably great, but for learners, it’s terrible even when it works.

    • deblokt says:

      The reason I use scaffolding in the tutorial is to minimize the “magic” code. Developers coming to .NET world from other technologies dislike the “magic” code (as they can’t understand what it does) and developers in general love to take ownership of the code in their app and change it as they seem fit. If you dislike scaffolding you can skip that part of the tutorial and just use NuGet package “Microsoft.AspNetCore.Identity.UI” containing Razor Class Library with all razor pages used by ASP.NET Core Identity. The rest of the tutorial is pretty much the same.

Leave a comment

Your email address will not be published. Required fields are marked *