Identityserver4 series | authorization code mode

Time:2021-11-30

1、 Foreword

In the last article aboutSimplified modeIn, it is requested in the form of browser through the clientIdentityServerThe service obtains the access token and requests to obtain the protected resource. However, because the token is carried in the URL, the security cannot be guaranteed. Therefore, we can consider other ways to solve this problem.

We passed OAuth 2.0Authorization code modeUnderstand that this model is different from the simplified model,The reason is that the authorization code mode does not directly return a token, but first returns an authorization code, and then requests a token according to the authorization code。 It seems safer.

So in this article, we will use theAuthorization codeMode, mainly for introductionIdentityServerProtect API resources,Authorization codeAccess API resources.

2、 First acquaintance


(picture source network)

It means that the third-party application first applies for an authorization code, and then uses the code to obtain the token to realize the communication with the resource server.

Look at a common process of QQ landing on a third-party website, as shown in the figure below:

2.1 scope of application

Authorization code mode is the most complete and rigorous authorization mode.

The authorization code mode is applicable to applications with a back-end. When the client requests a token according to the authorization code, the client password needs to be transferred in. In order to avoid the client password being exposed, the process of requesting a token needs to be put in the background.

2.2 authorization process:

+----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier      +---------------+
     |         -+----(A)-- & Redirection URI ---->|               |
     |  User-   |                                 | Authorization |
     |  Agent  -+----(B)-- User authenticates --->|     Server    |
     |          |                                 |               |
     |         -+----(C)-- Authorization Code ------(D)-- Authorization Code ---------'      |
     |  Client |          & Redirection URI                  |
     |         |                                             |
     |         |

Authorization code authorization process description

(A)The user accesses the third-party application, which directs the user to the authentication server

(B)The user selects whether to grant authorization to third-party applications

(C)Assuming that the user gives authorization, the authentication server directs the user to the redirection URI specified in advance by the third-party application, and carries an authorization code at the same time

(D)The third-party application receives the authorization code, brings the redirection URI in the previous step, and applies for an access token from the authentication server. This step is completed on the background server of the third-party application and is not visible to users

(E)The authentication server checks the authorization code and redirection URI, and sends an access token and a refresh token to the third-party application after confirmation

(F)After the access token expires, refresh the access token

2.2.1 process details


Access token request
(1) The user accesses the third-party application, which directs the user to the authentication server
(user actions: user access) https://client.example.com/cb Jump to the login address and select the authorization server (login)

Before authorization starts, it first generates the state parameter (random string). The client side will need to store this (cookie, session or other means) for use in the next step.

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
HTTP/1.1 Host: server.example.com

The generated authorization URL is as described above (as above). After requesting this address, redirect access to the authorization server, where response_ The type parameter is code, which indicates the authorization type and returns the code authorization code.

parameter Is it necessary meaning
response_type essential Indicates the authorization type. The value here is fixed as “code”
client_id essential client ID
redirect_uri Optional Uri representing redirection
scope Optional Indicates the scope of authorization.
state Optional Represents a random string. Any value can be specified. The authentication server will return this value

(2) Assuming that the user gives authorization, the authentication server directs the user to the redirection URI specified in advance by the third-party application, and carries an authorization code at the same time

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
parameter meaning
code Indicates authorization code; required. The validity period of the code should be very short. It is usually set to 10 minutes. The client can only use the code once, otherwise it will be rejected by the authorized server. This code corresponds to the client ID and redirection URI one by one.
state If the client as like as two peas, the response of the authentication server must be exactly the same.

(3) The third-party application receives the authorization code, brings the redirection URI in the previous step, and applies for an access token from the authentication server. This step is completed on the background server of the third-party application and is not visible to users

POST /token HTTP/1.1
Host: server.example.com
Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
parameter meaning
grant_type Indicates the authorization mode used. Required. The value here is fixed as “authorization_code”.
code Indicates the authorization code obtained in the previous step. Required.
redirect_uri Indicates the redirection URI, required, and must be consistent with the parameter value in step 1.
client_id Indicates the client ID; required.

(4) The authentication server checks the authorization code and redirection URI, and sends an access token and a refresh token to the third-party application after confirmation

HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache
     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"Bearer",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "example_parameter":"example_value"
     }
parameter meaning
access_token Indicates an access token. Required.
token_type Indicates the token type. The value is case insensitive and required. It can be bearer type or Mac type.
expires_in Indicates the expiration time, in seconds. If this parameter is omitted, the expiration time must be set in other ways.
refresh_token Indicates the update token, which is used to obtain the next access token. Optional.
scope Indicates the permission scope. If it is consistent with the scope applied by the client, this item can be omitted.

(5) After the access token expires, refresh the access token

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
parameter meaning
granttype Indicates the authorization mode used. The value here is fixed as “refresh token”. Required.
refresh_token Indicates the update token received earlier. Required.
scope Indicates the authorization scope of the application, which cannot exceed the scope of the previous application. If this parameter is omitted, it indicates that it is the same as the previous application.

3、 Practice

In the example practice, we will create an authorized access service and define an MVC client throughIdentityServerRequest an access token on and use it to access the API.

3.1 build authorization server service

Build authentication and authorization services

3.1.1 installing nuget package

IdentityServer4Package

3.1.2 configuration content

Create configuration content fileConfig.cs

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

    public static IEnumerable ApiScopes =>
        new ApiScope[]
    {
        new ApiScope("code_scope1")
    };

    public static IEnumerable ApiResources =>
        new ApiResource[]
    {
        new ApiResource("api1","api1")
        {
            Scopes={ "code_scope1" },
            Userclaims = {jwtclaimtypes. Role}, // add cliam role type
            ApiSecrets={new Secret("apipwd".Sha256())}
        }
    };

    public static IEnumerable Clients =>
        new Client[]
    {
        new Client
        {
            ClientId = "code_client",
            ClientName = "code Auth",

            AllowedGrantTypes = GrantTypes.Code,

            RedirectUris ={
                " http://localhost:5002/signin -Oidc ", // jump to the address of the logged in client
            },
            // RedirectUris = {" http://localhost:5002/auth.html "}, // jump to the address of the logged out client
            PostLogoutRedirectUris ={
                "http://localhost:5002/signout-callback-oidc",
            },
            ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) },

            AllowedScopes = {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                "code_scope1"
            },
            //Allow tokens to be passed through the browser
            AllowAccessTokensViaBrowser=true,
            //Whether consent authorization is required (false by default)
            RequireConsent=true
        }
    };
}

RedirectUris: the client address of the callback processing after successful login, and the data returned by the callback processing. There can be multiple.

PostLogoutRedirectUris: jump to the address of the client to log out to.

These two addresses are the addresses of the configured client and are encapsulated in the identityserver4 component. They are used for callback of login and logout respectively

the reason being thatAuthorization codeAuthorization, so we create several test users through code.

New test user fileTestUsers.cs

public class TestUsers
    {
        public static List Users
        {
            get
            {
                var address = new
                {
                    street_address = "One Hacker Way",
                    locality = "Heidelberg",
                    postal_code = 69118,
                    country = "Germany"
                };

                return new List
                {
                    new TestUser
                    {
                        SubjectId = "1",
                        Username = "i3yuan",
                        Password = "123456",
                        Claims =
                        {
                            new Claim(JwtClaimTypes.Name, "i3yuan Smith"),
                            new Claim(JwtClaimTypes.GivenName, "i3yuan"),
                            new Claim(JwtClaimTypes.FamilyName, "Smith"),
                            new Claim(JwtClaimTypes.Email, "[email protected]"),
                            new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                            new Claim(JwtClaimTypes.WebSite, "http://i3yuan.top"),
                            new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json)
                        }
                    }
                };
            }
        }
    }

Returns a collection of testusers.

After adding the configuration and testing user, we need to register the user with the identity server 4 service. Next, we will continue.

3.1.3 registration services

Add the following code to the configureservices method in startup.cs:

public void ConfigureServices(IServiceCollection services)
        {
            var builder = services.AddIdentityServer()
               .AddTestUsers(TestUsers.Users); // Add test user

            // in-memory, code config
            builder.AddInMemoryIdentityResources(Config.IdentityResources);
            builder.AddInMemoryApiScopes(Config.ApiScopes);
            builder.AddInMemoryApiResources(Config.ApiResources);
            builder.AddInMemoryClients(Config.Clients);

            // not recommended for production - you need to store your key material somewhere secure
            builder.AddDeveloperSigningCredential();
            services.ConfigureNonBreakingSameSiteCookies();
        }

3.1.4 pipeline configuration

Add the following code to the configure method in startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseStaticFiles();
            app.UseRouting();
            app.UseCookiePolicy();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseIdentityServer();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
            }); 
        }

The above content is a way to quickly build a simple identity server project service.

This is the last article on building the authorization server serviceSimplified modeWhat’s the difference?

  1. A client is defined in configAllowedGrantTypesThis attribute determines which mode the client can be accessed,GrantTypes.CodebyAuthorization code mode。 Therefore, in this article, we need to add a client to support authorization code mode(Authorization Code)。

3.2 build API resources

Protect API resources

3.2.1 quickly build an API project

3.2.2 installing nuget package

Identityserver4.accesstokenvalidation package

3.2.3 registration services

Add the following code to the configureservices method in startup.cs:

public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        services.AddAuthentication("Bearer")
          .AddIdentityServerAuthentication(options =>
          {
              options.Authority = "http://localhost:5001";
              options.RequireHttpsMetadata = false;
              options.ApiName = "api1";
              options.ApiSecret = "apipwd"; // Corresponds to the key in apiresources
          });
    }

Addauthentication configures bearer to the default mode and adds the identity authentication service to di.

Addidentityserver authentication adds the access token of the identityserver to the di for use by the identity authentication service.

3.2.4 pipeline configuration

Add the following code to the configure method in startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }    
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
            });
        }

Useauthentication adds authentication middleware to the pipeline;

Use authorization adds the startup authorization middleware to the pipeline to perform the authentication authorization function every time the host is called.

3.2.5 add API resource interface

[Route("api/[Controller]")]
[ApiController]
public class IdentityController:ControllerBase
{
    [HttpGet("getUserClaims")]
    [Authorize]
    public IActionResult GetUserClaims()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

Add [authorize] in the identitycontroller controller. When requesting resources, you need to authenticate and authorize before accessing them.

3.3 build MVC client

Implement client authentication and authorization to access resources

3.3.1 quickly build an MVC project

3.3.2 installing nuget package

Identityserver4.accesstokenvalidation package

3.3.3 registration services

To add support for openid connect authentication to the MVC application.

Add the following code to the configureservices method in startup.cs:

public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddAuthorization();

        services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
               . addcookie ("cookies") // use cookies as the preferred way to authenticate users
              .AddOpenIdConnect("oidc", options =>
              {
                  options.Authority = " http://localhost:5001 ";  // Authorization server address
                  options.RequireHttpsMetadata = false;  // Don't use HTTPS for the time being
                  options.ClientId = "code_client";
                  options.ClientSecret = "511536EF-F270-4058-80CA-1C89C192F69A";
                  options.ResponseType = "code"; // For authorization code
                  options.Scope.Add("code_scope1"); // Add authorized resources
                  options.SaveTokens = true; // Indicates that the obtained token is saved in the cookie
                  options.GetClaimsFromUserInfoEndpoint = true;
              });
         services.ConfigureNonBreakingSameSiteCookies();
    }
  1. AddAuthenticationInject and add authentication authorization. When user login is required, usecookieTo log in locally (via “cookies”DefaultScheme), andDefaultChallengeSchemeSet to “oidc”,

  2. useAddCookieAdd a handler that can handle cookies.

  3. stayAddOpenIdConnectUsed to configure executionOpenID ConnectProtocol handler and related parameters.AuthorityIndicates the previously built identity server authorization service address. Then we passedClientIdClientSecret, identify this client.SaveTokensIt is used to save the token obtained from the identity server to the cookie,tureIdentifying the ASP. Net core will automatically store the access and refresh token of the authentication session.

3.3.4 pipeline configuration

Then, to ensure that the authentication service performs authentication on each request, joinUseAuthenticationandUseAuthorizationreachConfigureIn, add the following code to the configure method in startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
            app.UseRouting();
            app.UseCookiePolicy();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

Useauthentication adds authentication middleware to the pipeline;

Use authorization adds the startup authorization middleware to the pipeline to perform the authentication authorization function every time the host is called.

3.3.5 add authorization

Select and add a controller in the homecontroller[Authorize]Attribute to one of the methods. When making a request, you need to authenticate and authorize before you can access.

[Authorize]
        public IActionResult Privacy()
        {
            ViewData["Message"] = "Secure page.";
            return View();
        }

Also modify the home view to display the user’s claim and cookie properties.

@using Microsoft.AspNetCore.Authentication

Claims


    @foreach (var claim in User.Claims)
    {
        @claim.Type
        @claim.Value
    }


Properties


    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
    {
        @prop.Key
        @prop.Value
    }

Access the privacy page, jump to the authentication service address, log in the account and password, and logout is used for user logout.

3.3.6 add resource access

stayHomeControllerThe controller adds interface methods for accessing API resources. Access API protected resources when making requests.

/// 
        ///Test request API resource (API1)
        /// 
        /// 
        public async Task getApi()
        {
            var client = new HttpClient();
            var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
            if (string.IsNullOrEmpty(accessToken))
            {
                Return JSON (New {MSG = "access token failed"});
            }
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            var httpResponse = await client.GetAsync("http://localhost:5003/api/identity/GetUserClaims"); 
            var result = await httpResponse.Content.ReadAsStringAsync();
            if (!httpResponse.IsSuccessStatusCode)
            {
                Return JSON (New {MSG = "request API1 failed", error = result});
            }
            return Json(new
            {
                MSG = "success",
                data = JsonConvert.DeserializeObject(result)
            });
        }

After obtaining the accesstoken, set the authentication of the client request header, access the protected address of the API resource, and obtain the resource.

3.4 effect

3.4.1 dynamic diagram

3.4.2 process

When the user accesses the MVC program, the user is directed to the authentication server,

To the authorization server on the client sideAuthorization EndpointDuring verification, we can find that the parameters attached to the request sent to the authorization server are the data we mentioned earlier (ClientID, redirect_url, type, etc.)

Continue to look down. After the user gives authorization and completes login, you can see that after login, the authorization server will redirect the URL address and bring an authorization code data to the MVC program.

Then MVC sends a request to the token endpoint of the authorized client. As can be seen from the figure below, this request contains the client_ id,client_ secret,code,grant_ Type and redirect_ URI, apply for an access token from the authorization server, and you can see in the response that the authorization server has checked the authorization code and the redirection address URI. After confirmation, it sends an access token and a refresh token to the third-party application

After obtaining the token, when accessing the protected resource, bring the token to request access, and you can respond successfully to obtain the user information resource.

4、 Summary

  1. This article mainly expoundsAuthorization code authorization, write an MVC client and request it in the form of browser through the clientIdentityServerRequest access token on to access protected API resources.
  2. Authorization code modeThe simplified mode solves the problem that the security cannot be guaranteed because the token is carried in the URL. However, the mode through the authorization code does not directly return the token, but first returns an authorization code, and then requests the token according to the authorization code. The process of requesting the token needs to be placed in the background, which is also more secure. Suitable for applications with back ends.
  3. This aspect will be introduced later. We will continue to explain the problem of database persistence and how to apply it in the API resource server and configure it in the client.
  4. If there is something wrong or incomprehensible, I hope you can make more corrections, ask questions, discuss together, keep learning and make common progress.
  5. Project address

5、 Attach

OpenID Connectdata

Authorization code data

Samesite problem solving

Recommended Today

Game case | application evolution and practice of service mesh in happy games

author Chen Zhiwei, Tencent level 12 background expert engineer, is now responsible for the public background technology research and development and team management of happy game studio. Rich experience in micro service distributed architecture and game background operation and maintenance research and development. preface The background of happy game studio is a distributed micro service […]