Click here to Skip to main content
13,146,014 members (42,687 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

2.9K views
6 bookmarked
Posted 12 Sep 2017

Identity Server 4 with ASP.NET Core 2.0

, 12 Sep 2017
Rate this:
Please Sign up or sign in to vote.
How to use Identity Server 4 with ASP.NET Core 2.0 Continue reading

Problem

How to use Identity Server 4 with ASP.NET Core 2.0

Solution

Note: I am assuming you have basic understanding about Identity Server. To know more refer to its documentation here.

I’ll implement 3 projects here:

  1. Server – running on port 5000
  2. API (i.e. protected resource) – running on port 5001
  3. Client – running on port 5002

Auth. Server

Create an empty web application and copy Quickstart, Views and wwwroot folders from GitHub. Create a Startup class:

public class Startup
    {
        public void ConfigureServices(
            IServiceCollection services)
        {
            services.AddMvc();
            services.AddIdentityServer()
                        .AddDeveloperSigningCredential(filename: "tempkey.rsa")
                        .AddInMemoryApiResources(Config.GetApiResources())
                        .AddInMemoryIdentityResources(Config.GetIdentityResources())
                        .AddInMemoryClients(Config.GetClients())
                        .AddTestUsers(Config.GetUsers());
        }

        public void Configure(
            IApplicationBuilder app, 
            IHostingEnvironment envloggerFactory)
        {
            app.UseIdentityServer();
            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }
    }

Create a class Config and add a methods for getting API, identity, clients and users:

public static class Config
    {
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("fiver_auth_api", "Fiver.Security.AuthServer.Api")
            };
        }

        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "fiver_auth_client",
                    ClientName = "Fiver.Security.AuthServer.Client",
                    ClientSecrets = { new Secret("secret".Sha256()) },

                    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
                    AllowOfflineAccess = true,
                    RequireConsent = false,

                    RedirectUris = { "http://localhost:5002/signin-oidc" },
                    PostLogoutRedirectUris = 
                      { "http://localhost:5002/signout-callback-oidc" },
                    
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "fiver_auth_api"
                    },
                }
            };
        }

        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "james",
                    Password = "password",
                    Claims = new List<Claim>
                    {
                        new Claim("name", "James Bond"),
                        new Claim("website", "https://james.com")
                    }
                }
            };
        }
    }

API

Create an API project and update its Startup class:

public class Startup
    {
        public void ConfigureServices(
            IServiceCollection services)
        {
            services.AddAuthentication(
               IdentityServerAuthenticationDefaults.JwtAuthenticationScheme)
                    .AddIdentityServerAuthentication(options =>
                     {
                         options.Authority = "http://localhost:5000"; // Auth Server
                         options.RequireHttpsMetadata = false; // only for development
                         options.ApiName = "fiver_auth_api"; // API Resource Id
                     });

            services.AddMvc();
        }

        public void Configure(
            IApplicationBuilder app, 
            IHostingEnvironment env)
        {
            app.UseAuthentication();
            app.UseMvcWithDefaultRoute();
        }
    }

Create a controller and protect using [Authorize] attribute:

[Authorize]
    [Route("movies")]
    public class MoviesController : Controller
    {

Client

Create a web application and update its Startup class:

public class Startup
    {
        public void ConfigureServices(
            IServiceCollection services)
        {
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            services.AddAuthentication(options =>
            {
                options.DefaultScheme = 
                    CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = 
                    OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(options =>
            {
                options.SignInScheme = 
                    CookieAuthenticationDefaults.AuthenticationScheme;
                options.Authority = "http://localhost:5000"; // Auth Server
                options.RequireHttpsMetadata = false; // only for development 
                options.ClientId = "fiver_auth_client"; // client setup in Auth Server
                options.ClientSecret = "secret";
                options.ResponseType = "code id_token"; // means Hybrid flow
                options.Scope.Add("fiver_auth_api");
                options.Scope.Add("offline_access");
                options.GetClaimsFromUserInfoEndpoint = true;
                options.SaveTokens = true;
            });

            services.AddMvc();
        }

        public void Configure(
            IApplicationBuilder app, 
            IHostingEnvironment env)
        {
            app.UseAuthentication();
            app.UseMvcWithDefaultRoute();
        }
    }

Add a controller to access the API:

[Authorize]
    public class HomeController : Controller
    {
        [AllowAnonymous]
        public IActionResult Index()
        {
            return View();
        }

        public async Task<IActionResult> Movies()
        {
            var accessToken = await HttpContext.GetTokenAsync("access_token");

            var client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new 
                AuthenticationHeaderValue("Bearer", accessToken);
            var content = await client.GetStringAsync("http://localhost:5001/movies");

            var model = JsonConvert.DeserializeObject<List<MovieViewModel>>(content);
            
            return View(model);
        }

        public async Task Logout()
        {
            await HttpContext.SignOutAsync(
               CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignOutAsync(
                OpenIdConnectDefaults.AuthenticationScheme);
        }
    }

Note: you can download the source code from my GitHub repository.

Discussion

IdentityServer4 website defines it as an OpenID Connect and OAuth 2.0 framework for ASP.NET Core that enables following features:

  • Centralise login logic for your applications
  • Single sign-on
  • Issues access tokens for APIs
  • Gateway to external identity providers like Google, Facebook etc.
  • Customisable

I’ll briefly discuss OAuth 2.0 and OpenID Connect here however for a much more in-depth discussion of OAuth 2.0 and OpenID Connect, I suggest looking at online courses and blog posts by Identity Server developer: Dominick Baier

OAuth 2 Flows

OAuth 2 provides several flows or grant types for various use cases. I personally group them into two categories; flows that require user interaction with authorisation server and flows that don’t’. Let’s first define terms I’ll be using when discussing flows:

Definitions

  • User: person using a Client and the owner of Protected Resource.
  • Client: the application that needs access to Protected Resource.
  • Protected Resource: the resource being protected from unauthorised access (e.g. Web API).
  • Server: server that authorizes User and returns Access Tokens.
  • Access Token: a secret key used to access Protected Resource.
  • Scopes: values based on which access to Protected Resource features is limited
  • Redirect URI: location where User returns after Auth. Server completes authorisation
  • Client ID: Client’s identifier registered with the Auth. Server.
  • Client Secret: Client’s secret registered with the Auth. Server. This must be kept confidential.

Requiring User Interaction with Auth. Server

Implicit Grant

The flow is usually used for native/local/mobile clients and has following high-level steps:

  1. User accesses the Client.
  2. User is redirected to Auth. Server.
  3. User provides username/password.
  4. User is redirected back to Client with an Access Token.
    • Note: Access Token is exposed to the user.
  5. Client access the Protected Resource using the Access Token.

Note: it is recommended to use Authorisation Code instead of Implicit Grant.

Authorisation Code

The flow is usually used for web application clients and has following high-level steps:

  1. User accesses the Client.
  2. User is redirected to Auth. Server.
  3. User provides username/password.
  4. User is redirected back to Client with a code.
    • Note: Code is exposed to the user.
  5. Client accesses the Auth. Server to exchange the code with an Access Token.
    • Note: Access Token is not exposed to the user.
  6. Client access the Protected Resource using the Access Token.

Not Requiring User Interaction with Auth. Server

Resource Owner Password Credentials

The flow is usually used for trusted clients and has following high-level steps:

  1. User access the Client and provide username/password.
    • Note: username/password is exposed to the Client.
  2. Client access the Auth. Server to exchange username/password with an Access Token.
  3. Client access the Protected Resource using the Access Token.

Client Credentials

The flow is usually used for client-server communication, without a human involvement, and has following high-level steps:

  1. Client access the Auth. Server to exchange Client ID and Secret with an Access Token.
  2. Client access the Protected Resource using the Access Token.

OpenID Connect

OpenID Connect is a layer on top of OAuth 2.0 and uses claims to communicate information about users. It provides services to verify user identity and obtain their profile information.

OpenID Connect uses OAuth 2.0 flows to obtain Identity Token, which asserts things like identity of the user (aka sub), issuing authority (aka iss), client (aka aud) and issue/expiry dates. The token is in JWT format and base-64 string.

Source Code

GitHub: https://github.com/TahirNaushad/Fiver.Security.AuthServer

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Tahir Naushad
Software Developer (Senior)
United Kingdom United Kingdom
Qualified and skilled professional with experience working as a Software Developer, Technical Lead and Architect. I have worked on windows, web and distributed applications using the latest set of technologies within the Microsoft .NET ecosystem. I have lead teams using agile methodologies and trained developers in writing well-designed and maintainable software applications.

Currently focusing on ASP.NET Core, C#, Azure, Angular and Domain Driven Design.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionCan you extend your example to include integrating this with an Azure Active Directory? Pin
Member 1342451013hrs 4mins ago
memberMember 1342451013hrs 4mins ago 
QuestionIssue regarding a variable Pin
Member 1166007321-Sep-17 21:14
memberMember 1166007321-Sep-17 21:14 
AnswerRe: Issue regarding a variable Pin
Tahir Naushad21-Sep-17 21:59
memberTahir Naushad21-Sep-17 21:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.170915.1 | Last Updated 12 Sep 2017
Article Copyright 2017 by Tahir Naushad
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid