skip to Main Content

I want my Identity Server 4 server to offer an additional service (e.g., "MyAdditionalService") for SOME of the registered clients. That service will be consumed by them through a custom endpoint to be defined on the server.

I am thinking of defining an API for my that service (e.g., named “myAdditionalService”) so that the access to such service can be granted to clients according to their configuration. However I am not sure how to restrict the access to the Endpoint (MVC – Action method) allowing only the clients (potentially on behalf of a user) that are allowed to consume the API.

I found out that I can do:

services.AddAuthorization(options =>
     {
       options.AddPolicy("MyAdditionalServicePolicy",
           policy => policy.RequireClaim("scope", 
           "myAdditionalService"));
      });

and use the attribute [Authorize("MyAdditionalServicePolicy")] to decorate the action method that is used to access such service. However, I don’t know can the server be the API at the same time or even if it is possible.

How can I implement this? It is confusing that the token service plays the role of the API as well, since it protects access to an action method or endpoint.

Thanks.


UPDATE:

My web app is an IdentityServerWithAspNetIdentity which already use the Authentication mechanism of Asp.net core Identity. For the sake of the example, the additional service my web app if offering to some registered clients is the list of Twitter friends of a user (Modeled on a controller called Twitter, action called ImportFriends) the api is consequently called “TwitterFriends”

As per suggestion in response below, I modified my Configure() method to have app.UseJwtBearerAuthentication(). I already had app.UseIdentity() and app.UseIdentityServer() as shown below:

        app.UseIdentity();
        app.UseIdentityServer();


        app.UseJwtBearerAuthentication(new JwtBearerOptions
        {
            AuthenticationScheme = "Bearer",
            Authority = Configuration["BaseUrl"],
            Audience = "TwitterFriends",
            RequireHttpsMetadata = false                 //TODO: make true, it is false for development only
        });

        // Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
        app.UseGoogleAuthentication(new GoogleOptions
        {
            AuthenticationScheme = "Google",
            SignInScheme = "Identity.External", // this is the name of the cookie middleware registered by UseIdentity()

And on a dedicated controller:

 [Authorize(ActiveAuthenticationSchemes = "Identity.Application,Bearer")]
//[Authorize(ActiveAuthenticationSchemes = "Identity.Application")]
//[Authorize(ActiveAuthenticationSchemes = "Bearer")]
[SecurityHeaders]
public class TwitterController : Controller
{...

but I am getting this in the log:

info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware
[7]
      Identity.Application was not authenticated. Failure message: Unprotect tic
ket failed
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed for user: (null).
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.A
uthorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
      Executing ChallengeResult with authentication schemes (Identity.Applicatio
n, Bearer).
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware
[12]
      AuthenticationScheme: Identity.Application was challenged.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[12]
      AuthenticationScheme: Bearer was challenged.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action IdentityServerWithAspNetIdentity.Controllers.TwitterContro
ller.ImportFriends (IdentityServerWithAspNetIdentity) in 86.255ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 105.2844ms 401

I have tried different combinations of the attribute but it seems that Identity.Application and Bearer don’t get along in this scenario: getting 401.

any help is appreciated.
Thanks..

2

Answers


  1. See this example on how to host an API in the same web app as IdentityServer.

    https://github.com/brockallen/IdentityServerAndApi

    In essence you need to add the JWT token validation handler:


    services.AddAuthentication()
    .AddJwtBearer(jwt =>
    {
    jwt.Authority = "base_address_of_identityserver";
    jwt.Audience = "name of api";
    });

    On the API itself you must select the JWT authentication scheme:


    public class TestController : ControllerBase
    {
    [Route("test")]
    [Authorize(AuthenticationSchemes = "Bearer")]
    public IActionResult Get()
    {
    var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToArray();
    return Ok(new { message = "Hello API", claims });
    }
    }

    If you want to enforce an additional authorization policy, you can either pass that into the [Authorize] attribute or call it imperatively.

    Login or Signup to reply.
  2. To achieve this, first you have to write some policy. Policy will define the boundry of accessibility of that specific api.
    So you will assign the some scope to registered clients. let’s say scope name is “ApiOnlyForRegisteredClients”.

    So we will create the policy as below:

            services.AddAuthorization(options =>
            {
                options.SetRegisteredClientsPolicy();
            }
    

    and

      private static void RequireScope(this AuthorizationPolicyBuilder authorizationPolicyBuilder, string[] values)
        {
            authorizationPolicyBuilder.RequireClaim("scope", values);
        }
    
    private static void SetRegisteredClientsPolicy(this AuthorizationOptions options)
    {
        options.AddPolicy(
                      OpenIdPolicies.Clients.RegisteredClients,
                      policyBuilder =>
                      {
                          policyBuilder.RequireAuthenticatedUser();
                          policyBuilder.RequireScope(new string[] { "ApiOnlyForRegisteredClients" });
                      });
    }
    

    Once it done, you are done with policy creation.

    Make sure while creating the access token, you are put the same value “ApiOnlyForRegisteredClients” in scope claim.

    Now we have to add one api and label it with [Authorize] attribute.

       [Authorize(AuthenticationSchemes = "Bearer", Policy = OpenIdPolicies.Clients.RegisteredClients)]
        public async Task<ActionResult<T>> Post(int userId, [FromBody] List<int> simRoleIds)
        {
        }
    

    Now we have to add jwt authentication middleware.

    .AddJwtBearer("Bearer", options =>
       {
           options.Authority = configuration["AuthorityAddresses"];
           options.RequireHttpsMetadata = Convert.ToBoolean(configuration["RequireHttpsMetadata"]);
           options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
           {
               TokenDecryptionKey = new X509SecurityKey()
               ValidAudiences = apiResources.Select(x => x.ResourceName).ToList(),
               ValidIssuers = new List<string> { authorityAddressWithHttps.Uri.OriginalString, authorityAddressWithBasePathHttps.Uri.OriginalString, configuration["AuthorityAddresses"] }
           };
       })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search