Thursday, August 5, 2021

Basic Client Authentication Using Identity Server 4 - The Server

 The aim of this tutorial is to use Identity Server 4 for client authentication. We will build two different VS projects, one will be server and other will be client. The server will host IdentityServer4 and do the task of authentication. The authentication will be based on client credentials, hence user , user id and password will not be required. 

Building the Identity Server

1. Create a new asp.net core web application project (the screen shots pertain to Visual Studio 2019). 




2. Once the project is created, add nuget packages IdentityServer4 and IdentityServer4.EntityFramework. 



3. Open startup.cs. 
At the end of  startup.cs add a new public class IS4Data. 
We will put the methods required by IS4 in this class. 

public class IS4Data
    {
        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new List<ApiScope>
             {
                 new ApiScope(name: "read",   displayName: "Read your data."),
                 new ApiScope(name: "write",  displayName: "Write your data."),
                 new ApiScope(name: "delete", displayName: "Delete your data."),
                 new ApiScope(name: "allow_weather_api", displayName: "Api Scope.")
             };
        }
        public static IEnumerable<ApiResource> GetApiResourses()
        {
            return new List<ApiResource>
            {
                 new ApiResource()
                        {
                        Name = "weatherapiresource",   //This is the name of the API
                        //Description = "This is the invoice",
                        Enabled = true,
                        DisplayName = "Weather API Resource",
                        Scopes = new List<string> { "allow_weather_api"},
                        }

        };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client
                {
                    ClientId = "ABC Company Inc.",
                    AllowedGrantTypes = {GrantType.ClientCredentials },
                    ClientSecrets =  {
                        new Secret("ThisIsASecretString".Sha256())
                    }
                    ,
                    AllowedScopes = { "allow_weather_api" }
                }
            };
        }

    }


4. In the same file startup.cs, add the following code at the end of the ConfigureServices  method: 

            services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryApiResources(IS4Data.GetApiResourses())
                .AddInMemoryApiScopes(IS4Data.GetApiScopes())
                .AddInMemoryClients(IS4Data.GetClients());

5. In the same file startup.cs, add a single statement in Configure method: 
            app.UseIdentityServer();

6. Run the project. This will take you to the browser on the weatherforcast api output. 

7. To test the server, use the .well-known/openid-configuration api. Supposing the project is running on localhost on the port 44368, you can browse to the URL  

https://localhost:44368/.well-known/openid-configuration


This will give an output like : 
{"issuer":"https://localhost:44368","jwks_uri":"https://localhost:44368/.well-known/openid-configuration/jwks","authorization_endpoint":"https://localhost:44368/connect/authorize","token_endpoint":"https://localhost:44368/connect/token","userinfo_endpoint":"https://localhost:44368/connect/userinfo","end_session_endpoint":"https://localhost:44368/connect/endsession","check_session_iframe":"https://localhost:44368/connect/checksession","revocation_endpoint":"https://localhost:44368/connect/revocation","introspection_endpoint":"https://localhost:44368/connect/introspect","device_authorization_endpoint":"https://localhost:44368/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["read","write","delete","allow_weather_api","offline_access"],"claims_supported":[],"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"],"id_token_signing_alg_values_supported":["RS256"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true}

If opened in POSTMAN with a GET request, the output will look like 
{
    "issuer""https://localhost:44368",
    "jwks_uri""https://localhost:44368/.well-known/openid-configuration/jwks",
    "authorization_endpoint""https://localhost:44368/connect/authorize",
    "token_endpoint""https://localhost:44368/connect/token",
    "userinfo_endpoint""https://localhost:44368/connect/userinfo",
    "end_session_endpoint""https://localhost:44368/connect/endsession",
    "check_session_iframe""https://localhost:44368/connect/checksession",
    "revocation_endpoint""https://localhost:44368/connect/revocation",
    "introspection_endpoint""https://localhost:44368/connect/introspect",
    "device_authorization_endpoint""https://localhost:44368/connect/deviceauthorization",
    "frontchannel_logout_supported"true,
    "frontchannel_logout_session_supported"true,
    "backchannel_logout_supported"true,
    "backchannel_logout_session_supported"true,
    "scopes_supported": [
        "read",
        "write",
        "delete",
        "allow_weather_api",
        "offline_access"
    ],
    "claims_supported": [],
    "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"
    ],
    "id_token_signing_alg_values_supported": [
        "RS256"
    ],
    "subject_types_supported": [
        "public"
    ],
    "code_challenge_methods_supported": [
        "plain",
        "S256"
    ],
    "request_parameter_supported"true
}




You can also get token by calling /connect/token endpoint

The required credentials are grant_type, client_id and client_scecret






All the code chages are in startup.cs only and below is the complete startup.cs file. 


using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Server
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to
        // add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryApiResources(IS4Data.GetApiResourses())
                .AddInMemoryApiScopes(IS4Data.GetApiScopes())
                .AddInMemoryClients(IS4Data.GetClients());
        }

        // This method gets called by the runtime. Use this method to
        // configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();
            app.UseIdentityServer();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }


    public class IS4Data
    {
        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new List<ApiScope>
             {
                 new ApiScope(name: "read",   displayName: "Read your data."),
                 new ApiScope(name: "write",  displayName: "Write your data."),
                 new ApiScope(name: "delete", displayName: "Delete your data."),
                 new ApiScope(name: "allow_weather_api", displayName: "Api Scope.")
             };
        }
        public static IEnumerable<ApiResource> GetApiResourses()
        {
            return new List<ApiResource>
            {


                new ApiResource()
                        {
                        Name = "weatherapiresource",   //This is the name of the API
                        //Description = "This is the invoice",
                        Enabled = true,
                        DisplayName = "Weather API Resource",
                        Scopes = new List<string> { "allow_weather_api"},
                        }


        };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client
                {
                    ClientId = "ABC Company Inc.",
                    AllowedGrantTypes = {GrantType.ClientCredentials },
                    ClientSecrets =  {
                        new Secret("ThisIsASecretString".Sha256())
                    }
                    ,
                    AllowedScopes = { "allow_weather_api" }
                }
            };
        }

    }
}

No comments:

Post a Comment

 using Microsoft.AspNetCore.Mvc; using System.Xml.Linq; using System.Xml.XPath; //<table class="common-table medium js-table js-stre...