I - Understanding CAT and Identity Server

I - Understanding CAT and Identity Server

What is CAT?

The CAT architecture pattern: To ensure a separation of concerns, the CAT pattern is made up of three modular services: Clients, Api Resources, and Token Service. To ensure the application is effective and users’ rights are protected, each modular service has its own powers and responsibilities, including working with the other modular services.

  1. Clients — this service category can be desktop, web, or mobile app

  2. ApiResources — this service category provides JSON data via REST API, GraphQL, etc.

  3. TokenService — this service category issues JWT and can facilitate Single-Signon using identity providers such as Azure AD, Okta, etc.

Below is a diagram illustrating how these three components interact with each other.

What is Duende identityServer?

IdentityServer (now called Duende, but commonly referred to as Identity Server) is an OpenID Connect and OAuth 2.0 framework that provides a set of services and middleware for ASP.NET Core apps. Duende.IdentityServer supports multiple protocol flows or grant types such as Authorization Code, Client Credentials, Refresh Token, Implicit, etc.

Using a CAT architecture turns the structure above into the flow illustrated below.

Why JWT?

Because it's a simple and great technology for authenticating APIs and server-to-server authorization. Please keep in mind that a JWT guarantees data ownership but not encryption; the JSON data you store in a JWT can be seen by anyone that intercepts the token, as it’s just serialized, not encrypted. In other words, using JWT doesn't make the API invulnerable. For this reason, an extra layer of protection is coupled with JWT to secure all our network traffic with an HTTPS connection.

Client Credentials Flow

Client Credentials Flow is a process in which client apps use client_id, client_secret, and sometimes scope in exchange for an access_token to access a protected resource. This flow is the recommended way to secure APIs easily without a particular user connected. This approach is better in server-to-server scenarios when interconnected internal applications within a system need to authenticate without interaction with the user.

The basic responsibilities of IdentityServer

Identity Server is an all-in-one Security Solution for your Projects. Here are its major features and responsibilities.

  • protect your resources

  • authenticate users using a local account store or via an external identity provider

  • provide session management and single sign-on

  • manage and authenticate clients

  • issue identity and access tokens to clients

  • validate tokens

Defining the Basics

User

A user is a human that is using a registered client to access resources.

public static List<TestUser> TestUsers =>
    new List<TestUser>
    {
        new TestUser
        {
            SubjectId = "1144",
            Username = "mukesh",
            Password = "mukesh",
            Claims =
            {
                new Claim(JwtClaimTypes.Name, "Mukesh Murugan"),
                new Claim(JwtClaimTypes.GivenName, "Mukesh"),
                new Claim(JwtClaimTypes.FamilyName, "Murugan"),
                new Claim(JwtClaimTypes.WebSite, "http://codewithmukesh.com"),
            }
        }
};

Client

A client is a piece of software that requests tokens from IdentityServer - either for authenticating a user (requesting an identity token) or accessing a resource (requesting an access token). A client must be first registered with IdentityServer before it can request tokens. Examples for clients are web applications, native mobile or desktop applications, SPAs, server processes, etc.

public static IEnumerable<Client> Clients =>
    new Client[]
    {
        new Client
        {
            ClientId = "cwm.client",
            ClientName = "Client Credentials Client",
            AllowedGrantTypes = GrantTypes.ClientCredentials,
            ClientSecrets = { new Secret("secret".Sha256()) },
            AllowedScopes = { "myApi.read" }
        },
    };

Discovery Client

It provides you with a list of services that match a specific set of criteria and allows you to connect to the services. The Discovery Endpoint is used to retrieve metadata about identityServer, and it returns the authorized endpoint, issuer name, key, material, and token endpoint.

OpenID Discovery Document

The OpenID Connect Discovery Document is available for all OpenID Providers at /.well-known/openid-configuration. This document contains the definition of your IdentityServer such as the token endpoint (the endpoint that you send POST to, to retrieve access tokens), supported scopes, the URL of the running IdentityServer, and so on.

{
    "issuer": "https://localhost:44354",
    "jwks_uri": "https://localhost:44354/.well-known/openid-configuration/jwks",
    "authorization_endpoint": "https://localhost:44354/connect/authorize",
    "token_endpoint": "https://localhost:44354/connect/token",
    "userinfo_endpoint": "https://localhost:44354/connect/userinfo",
    "end_session_endpoint": "https://localhost:44354/connect/endsession",
    "check_session_iframe": "https://localhost:44354/connect/checksession",
    "revocation_endpoint": "https://localhost:44354/connect/revocation",
    "introspection_endpoint": "https://localhost:44354/connect/introspect",
    "device_authorization_endpoint": "https://localhost:44354/connect/deviceauthorization",
    "frontchannel_logout_supported": true,
    "frontchannel_logout_session_supported": true,
    "backchannel_logout_supported": true,
    "backchannel_logout_session_supported": true,
    "scopes_supported": [
        "app.api.whatever.read",
        "app.api.whatever.write",
        "app.api.whatever.full",
        "app.api.weather",
        "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
}

HTTP Client

The HttpClient class handles sending and receiving HTTP requests and responses from resources identified by a URL. With it, you can use the async feature of .Net.

Client credentials

Machine-to-machine/server-to-server communication tokens are always requested on behalf of a client, no interactive user is present. Using the client credentials grant type, you send a token request to the token endpoint. Get the client's access token back. With the help of the client Id and secret, the client authenticates with the token endpoint.

Resources

Resources are something you want to protect with IdentityServer - either identity data of your users, or APIs. Every resource must have a unique name - and clients use this name to specify to which resources they want to be granted access.

public static IEnumerable<IdentityResource> IdentityResources =>
    new IdentityResource[]
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
    };

Identity data

Identity information (aka claims) about a user, e.g. name or email address.

APIs

APIs resources represent functionality a client wants to invoke - typically modeled as Web APIs, but not necessarily.

public static IEnumerable<ApiResource> ApiResources =>
    new ApiResource[]
    {
        new ApiResource("myApi")
        {
            Scopes = new List<string>{ "myApi.read","myApi.write" },
            ApiSecrets = new List<Secret>{ new Secret("supersecret".Sha256()) }
        }
    };

API Scopes

API can have scopes. Scopes in the context of, what the authorized user can do. For example, we can have 2 scopes for now – Read, Write.

public static IEnumerable<ApiScope> ApiScopes =>
    new ApiScope[]
    {
        new ApiScope("myApi.read"),
        new ApiScope("myApi.write"),
    };

Identity Token

An identity token represents the outcome of an authentication process. It contains at a bare minimum an identifier for the user (called the sub aka subject claim) and information about how and when the user authenticated. It can contain additional identity data.

Access Token

An access token allows access to an API resource. Clients request access tokens and forward them to the API. Access tokens contain information about the client and the user (if present). APIs use that information to authorize access to their data.

POST /connect/token HTTP/1.1
Host: localhost:44354
Content-Type: application/x-www-form-urlencoded
client_id=t8agr5xKt4$3&
client_secret=eb300de4-add9-42f4-a3ac-abd3c60f1919&
grant_type=client_credentials&
scope=app.api.whatever.read app.api.whatever.write
{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IlJMUGVHQVhlQkluZFdsdVFwclBBdEEiLCJ0eXAiOiJhdCtqd3QifQ.eyJuYmYiOjE1ODE5MjM5MzQsImV4cCI6MTU4MTkyNzUzNCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNTQiLCJhdWQiOiJhcHAuYXBpLndoYXRldmVyIiwiY2xpZW50X2lkIjoidDhhZ3I1eEt0NCQzIiwic2NvcGUiOlsiYXBwLmFwaS53aGF0ZXZlci5yZWFkIiwiYXBwLmFwaS53aGF0ZXZlci53cml0ZSJdfQ.lWQflS4xUe8hPcPHPjaTenOu7XkSWtsLHY4IGqLxu4AUtM0Ki8XSS2vEaLBfp5rIxgvjSMKbwv2SMJJCOPeB8Ck0L62ohldmAvs2fhiYYNNg4_Oz3ljVfbQz6zdP8xAVc6LKXXVM3Ed8GO_yRAgFDOOCfpiimj81h4QPd8yCrpWvHDihxsvwtCVGBQVQRMEv85fYhWoJ8qeX4sj2sW-dcuLzj-DFsPfqcX-BggXw5O4JVpmQ8QEUNCX1NCkLe8wYhu4GCAwTLK-umhirCSNzBhmAuIV3sXoPWa6VsYK4qJn8OVEOG0vDr8Jd394uauc6NYyxcZsf1qgxPa8-LRm_xA",
    "expires_in": 3600,
    "token_type": "Bearer",
    "scope": "app.api.whatever.read app.api.whatever.write"
}

Grant Type

The Grant Type describes how the client communicates with the resources or the way it talks to the authentication server or identity server in our case. You can specify the grant types a client can use via the AllowedGrantype property on the Client Configuration. It is possible to configure a client to accept multiple grant types for a single user.

Persisted Grants

Many grant types require persistence in IdentityServer. These include authorization codes, refresh tokens, reference tokens, and remembered user consents. Internally in IdentityServer, the default storage for these grants is in a common store called the persisted grants store.

Resource owner password grant type

You can use the Resource Owner Password to request tokens on behalf of a user to send the user name and password to the token endpoint. Non-interactive authentication. A user is involved with a trusted 1st party app (mobile app) that handles the user's credentials (password). By implementing the IResourcerOwnerPasswordValidator interface, you must provide code to validate the user name and password.

Authorization code

Tokens are retrieved back to authorization channels like Google and Facebook after the user login the user name and password are sent back to the authorization code. Authentication by authorization code is also supported.

Implicit

This grant type is optimized to be used with browser-based applications, server-side applications, and JavaScript applications. Suppose the client redirects the user and the user to identityServer and the user login with credentials username and password, the identity server returns the token. In that case, the token is transmitted via the browser to access the resource.

Hybrid

Hybrid is the combination of implicit and authorization codes. In other words, it is a combination of multiple grant types. Identity tokens are transmitted via the browser and contain a single protocol response and signature