Modern Identity Paradigm – Introduction (Part 2)

In this demo, we will see how to setup ThinkTecture Identity Server and will write few clients (mvc web application and a console application) to consume it.

This version of our implementation will use only in memory data for User, Clients and Scopes, just to focus on core implementation of Identity Server. I’ll write separate posts for customizing the identity server to connect to database for user data and other customizations.

You can find that source code at Github as well.

Let’s get started…

Creating Projects

Identity Server

Here we will create a project that will act as Identity server with in memory data.

Creating Visual Studio Project

Create a new solution in Visual Studio by selecting ASP.NET Web Application and give appropriate names

Create Web Application Project for Identity Server

Choose Empty template with no authentication 

Choose empty template with no authentication

Adding References

Now you need to install following packages. You can use either Package manager console or Manage Nuget Packages window. However I would recommend you to use Install-Package command in package manager console

  1. Thinktecture.IdentityServer3
  2. Microsoft.Owin.Host.SystemWeb

Adding Users

We will create a new Users class that will act as user data source. We will use in-memory data for now, just to focus on how ThinkTecture Identity server works. Users class will look like this

static class Users

    {

        public static List<InMemoryUser> Get()

        {

            var users = new List<InMemoryUser>

            {

                new InMemoryUser{

                    Subject = "818727",

                    Username = "itua",

                    Password = "itua",

                    Claims = new[]

                    {

                        new Claim(Constants.ClaimTypes.Name, "Umair Ali"),

                        new Claim(Constants.ClaimTypes.GivenName, "Umair"),

                        new Claim(Constants.ClaimTypes.Email, "umair@mail.com"),

                        new Claim(Constants.ClaimTypes.Role, "Developer")

                    }

                },

                new InMemoryUser{

                    Subject = "88421113",

                    Username = "itaa",

                    Password = "itaa",

                    Claims = new[]

                    {

                        new Claim(Constants.ClaimTypes.Name, "Anis Ahmed"),

                        new Claim(Constants.ClaimTypes.GivenName, "Anis"),

                        new Claim(Constants.ClaimTypes.Email, "anis@mail.com"),

                        new Claim(Constants.ClaimTypes.Role, "Developer"),

                        new Claim(Constants.ClaimTypes.Role, "Admin")

                    }

                },

            };

            return users;

        }

    }

Clients

Clients can be multiple applications those are bound to get authenticated by the Identity Server.  These clients need to be registered into the system so that Identity server can recognize and act properly at the time of login request

Again we will use in memory data for clients and its data source class will look like

public class Clients

    {

        public static List<Client> Get()

        {

            return new List<Client>

            {

                // Resource Credentials - console app

                new Client

                {

                    Enabled = true,

                    ClientName = ApplicationConstants.ConsoleAppClientName,

                    ClientId = ApplicationConstants.ConsoleAppClientId,

                    AccessTokenType = AccessTokenType.Reference,

                    Flow = Flows.ResourceOwner,

                    ClientSecrets = new List<ClientSecret>

                    {

                        new ClientSecret(ApplicationConstants.ConsoleAppClientSecret.Sha256())

                    }

                },

 

                // Implicit Flow - web app

                new Client

                {

                    Enabled = true,

                    ClientName = ApplicationConstants.WebAppClientName,

                    ClientId = ApplicationConstants.WebAppClientId,

                    ClientSecrets = newList<ClientSecret>

                    {

                        new ClientSecret(ApplicationConstants.WebAppClientSecret.Sha256())

                    },

                    Flow = Flows.Implicit,                   

                    RequireConsent = false,

                    IdentityTokenLifetime = 360,

                    AccessTokenLifetime = 3600,

                    RedirectUris = new List<string>

                    {

                        ApplicationConstants.UrlBaseWeb

                    },

                    PostLogoutRedirectUris = new List<string>

                    {

                        ApplicationConstants.UrlBaseWeb

                    }

                }

            };

        }

    }

Scopes

In claims based authentication systems, Scopes are used to limit the required claims. A user can have multiple claims based on different systems at a time. That is why scopes were introduced to instruct authentication system to only return claims those are relevant to specific system or client.

Scopes class will look like below, that will actually work as scopes data source.

public class Scopes

    {

        public static IEnumerable<Scope> Get()

        {

            var scopes = new List<Scope>();

            scopes.AddRange(StandardScopes.All);

            var profileScope = new Scope

            {

                Name = ApplicationScopes.AppProfile,

                DisplayName = "User App Profile Scope",

                Description = "Scope for Application specific user details.",

                Type = ScopeType.Identity,

                IncludeAllClaimsForUser = true,

                ShowInDiscoveryDocument = true,

                Claims = newList<ScopeClaim>

                {

                    new ScopeClaim(ApplicationClaimTypes.DisplayName, true)

                }

            };

            scopes.Add(profileScope);

            var webScope = newScope

            {

                Name = ApplicationScopes.MvcApp,

                DisplayName = "Web App Scope",

                Description = "Scope for Web Application",

                Type = ScopeType.Resource,

                IncludeAllClaimsForUser = true,

                ShowInDiscoveryDocument = true,

                Claims = new List<ScopeClaim>

                {

                    new ScopeClaim(ApplicationClaimTypes.Role, true),

                    new ScopeClaim(ApplicationClaimTypes.Values, true)

                }

            };

            scopes.Add(webScope);

            var consoleScope = newScope

            {

                Name = ApplicationScopes.ConsoleApp,

                DisplayName = "Console App Scope",

                Description = "Scope for Console Application",

                Type = ScopeType.Resource,

                IncludeAllClaimsForUser = true,

                ShowInDiscoveryDocument = true,

                Claims = new List<ScopeClaim>

                {

                    new ScopeClaim(ApplicationClaimTypes.Values, true)

                }

            };

            scopes.Add(consoleScope);

            return scopes;

        }

    }

Startup

Finally, we will add OWIN Startup class in to this project by, Add > New Item > Owin Startup Class.

First of all, we will setup a log provider for Identity Server, which proves very useful in case error detection, Add following line in Configuration method.

LogProvider.SetCurrentLogProvider(newDiagnosticsTraceLogProvider());

It will also require a few configurations in web.config file, inside configuration node, i.e.

<system.diagnostics>

    <traceautoflush="true">

      <listeners>

        <addname="TextWriter"

             type="System.Diagnostics.TextWriterTraceListener"

             initializeData="D:\IdentityServerLogs.log" />

      </listeners>

    </trace>

  </system.diagnostics>

Then we will initialize the (in memory) data source for User, Client, Scope and then certificate options

var factory = InMemoryFactory.Create(

                users: Users.Get(),

                clients: Clients.Get(),

                scopes: Scopes.Get());

var options = new IdentityServerOptions

            {

                RequireSsl = false,

                SigningCertificate = Certificate.Load(),

                Factory = factory,

            };

app.UseIdentityServer(options);

 

Now Identity server is ready to serve the clients. You will see similar to blow screenshot if you run this project.

Identity server home screen


 

Console Client

Now we will create a console based client that will try to authenticate from identity server and will decrypt the JWT token to see what claims it is getting in response.

Add a new console application project in the solution and add following references in it

  1. ThinkTecture.IdentityModel.Client
  2. Microsoft.Net.Http
  3. System.IdentityModel.Tokens.Jwt

Now you can request access token from server like

var client = new OAuth2Client(

                new Uri(ApplicationConstants.TokenEndpoint),

                ApplicationConstants.ConsoleAppClientId,

                ApplicationConstants.ConsoleAppClientSecret);

You can also view claims inside the token by calling below method.

GetClaimsFromToken(response.AccessToken, ApplicationConstants.UrlBaseAuth + "/resources");

 

Output will look something like

Console client output 

 

That is all for today, but there is lot more to come. In next post, I’ll tell you guys how to create a web application client that can authenticate from our ThinkTecture Identity Server. Then we will be good enough to move to authorization part of it.

Looking forward to hear from you guys to improve the quality of this post.

 

 

comments powered by Disqus