[.NET Core] IdentityServer4(1) of ASP.NET Core: Quick Start

The IdentityServer4 in this article is based on the jenkins in the previous section for docker automated deployment.
Used MariaDB, EF Core, AspNetIdentity, Docker

Demo site: https://sso.neverc.cn
Demo source code: https://github.com/NeverCL/Geek.IdentityServer4

Introduction

OpenID Connect: Commonly used authentication protocols are SAML2p, WS-Federation and OpenID Connect – SAML2p. OpenID Connect is the latest of these protocols.

OAuth 2.0: OAuth 2.0 is an authorization protocol. The protected API interface can be accessed through the Access Token.

OpenID Connect is very similar to OAuth 2.0, in fact, OpenID Connect is an extension on top of OAuth 2.0.
The two fundamental security concerns of authentication and API access are combined into a single protocol - often a single round-trip to the security token service.

IdentityServer4 is based on ASP.NET Core 2's implementation of these two protocols.

Support Specs: https://identityserver4.readthedocs.io/en/release/intro/specs.html

Key words

IdentityServer:提供OpenID Connect and OAuth 2.0 protocols.

User: User in IdentityServer

Client: Third-party applications, including web applications, native mobile or desktop applications, SPAs etc.

Resource: Contains Identity data and APIs. This is the ID in the authentication authorization.

Identity Token: Identity authentication information, including at least the user's sub claim.

Access Token: identifies authorization information, which can include client and user claim information.

Authorization method

Client Credentials

Client Credentials is the easiest way to authorize.

step:

  1. Create IdentityServer
    1. Define APIs
    2. Define Client
  2. Create API
    1. Define Authentication
  3. use Client
    1. Request Token
    2. Use Token

IdentityServer:

dotnet new web -o Geek.IdentityServer4 && dotnet add Geek.IdentityServer4 package IdentityServer4

Startup:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients());
...
app.UseIdentityServer();

Config:

public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("api1")
    };
}

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        new Client
        {
            ClientId = "client",
            AllowedGrantTypes = GrantTypes.ClientCredentials,
            ClientSecrets = { new Secret("secret".Sha256()) },
            Claims = { new Claim("name","名称") },
            AllowedScopes = { "api1" }
        },
    }
}

API:

dotnet new web -o Geek.Api && dotnet add Geek.Api package IdentityServer4.AccessTokenValidation

Startup:

services.AddMvc();
services.AddAuthentication("Bearer")//AddIdentityServerAuthentication 默认SchemeName:Bearer
    .AddIdentityServerAuthentication(opt =>
    {
        opt.ApiName = "api1";
        opt.Authority = "https://sso.neverc.cn";
    });
...
app.UseAuthentication();
app.UseMvc();

Controller:

[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

Client:

dotnet new web -o Geek.Client && dotnet add Geek.Client package IdentityServer4.IdentityModel

Program:

var disco = await DiscoveryClient.GetAsync("https://sso.neverc.cn");
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var response = await client.GetAsync("http://localhost:5001/identity");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));

ResourceOwnerPassword

This authentication method requires the User to provide a user name and password, so the Client can only use this method for very trusted applications.

step:

  1. Define RO Client and User
  2. use Client

Identity Server

Config:

public static IEnumerable<Client> GetClients()
{
    ...
    new Client
    {
        ClientId = "ro.client",
        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

        ClientSecrets = { new Secret("secret".Sha256()) },
        AllowedScopes = { "api1" }
    }
}
public static List<TestUser> GetUsers()
{
    return new List<TestUser>
    {
        new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "password",
        }
    }
}

Startup:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddTestUsers(Config.GetUsers());

Client

var disco = await DiscoveryClient.GetAsync("https://sso.neverc.cn");
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1");
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var response = await client.GetAsync("http://localhost:5001/identity");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));

Distinguish Client Credentials and ResourceOwnerPassword by sub claim

Implicit

Implicit is an implicit mode, and the id_token is directly transmitted through the browser

step:

  1. Configure IdentityServer
    1. Define IdentityResources
    2. define mvc client
    3. Add Mvc UI
  2. Create mvc client

IdentityServer

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

...
new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",
    AllowedGrantTypes = GrantTypes.Implicit,
    ClientSecrets = { new Secret("secret".Sha256()) },
    RedirectUris = { "http://localhost:5002/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
    }
}
services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddTestUsers(Config.GetUsers())
    .AddInMemoryIdentityResources(Config.GetIdentityResources());

Add MvcUI:

In the IdentityServer project powershell executes:

iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))

MvcClient

public void ConfigureServices(IServiceCollection services)
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
    services.AddMvc();
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", options =>
    {
        options.SignInScheme = "Cookies";
        options.Authority = "https://sso.neverc.cn";
        options.ClientId = "mvc";
        options.SaveTokens = true;
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();
    app.UseMvcWithDefaultRoute();
}
public class HomeController : ControllerBase
{
    [Authorize]
    public ActionResult Index()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

Hybrid

In Implicit mode, id_token transmission in browser is applicable, but access_token should not be exposed in browser.
Hybrid mode is based on Implicit, and then transmits code, and applies code mode to obtain access_token.

step:

  1. Define Client
  2. use Client

IdentityServer configuration

Config:

new Client
{
    ClientId = "hybrid",
    AllowedGrantTypes = GrantTypes.Hybrid,
    ClientSecrets = { new Secret("secret".Sha256()) },
    RedirectUris           = { "http://localhost:5002/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
    AllowedScopes = {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api1"
    },
};

MvcClient configuration

Startup:

.AddOpenIdConnect("oidc", options =>
{
    options.SignInScheme = "Cookies";
    options.Authority = "https://sso.neverc.cn";
    options.ClientId = "mvc";
    options.ClientSecret = "secret";
    options.ResponseType = "code id_token";
    options.SaveTokens = true;
    options.Scope.Add("api1");
});

Controller:

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

    var client = new HttpClient();
    client.SetBearerToken(accessToken);
    var content = await client.GetStringAsync("http://localhost:5001/identity");

    ViewBag.Json = JArray.Parse(content).ToString();
    return View("json");
}

After the login is completed, you can call the CallApiUsingUserAccessToken through the access_token obtained from the authentication to call the API service.

Summarize

This article provides a basic introduction to IdentityServer4.
In fact, IdentityServer4 can also be used very flexibly in combination with ASP.NET Identity and EF Core.
In addition, it is based on ASP.NET Core, so IdentityServer4 also supports cross-platform.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325642408&siteId=291194637