Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duende.IdentityServer from Blazor WebAssembly App protecting ASP.NET Core API using Client Credentials with custom allowed scope - invalid_scope #44122

Open
1 task done
Ogglas opened this issue Sep 22, 2022 · 8 comments
Labels
area-identity Includes: Identity and providers External This is an issue in a component not contained in this repository. It is open for tracking purposes. feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly
Milestone

Comments

@Ogglas
Copy link

Ogglas commented Sep 22, 2022

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

I have a Blazor WebAssembly App created with Microsoft Visual Studio with these specifications: Target Framework .NET 6.0, Authentication Type Individual Accounts and ASP.NET Core Hosted:

Using this answer I have been able to add Client Credentials flow

https://stackoverflow.com/a/67324222/3850405

I removed this from appsettings.json:

"Clients": {
  "WebApplication4.Client": {
    "Profile": "IdentityServerSPA"
  }
}

Edit Startup.cs or Program.cs:

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
        options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
        {
            builder.WithRedirectUri("/authentication/login-callback");
            builder.WithLogoutRedirectUri("/authentication/logout-callback");
        });
        options.Clients.Add(new Duende.IdentityServer.Models.Client
        {
            ClientId = "WebApplication4.Integration",
            AllowedGrantTypes = { GrantType.ClientCredentials },
            ClientSecrets = { new Secret("MySecretValue".Sha256()) },
            AllowedScopes = { "WebApplication4.ServerAPI"}
        });
    });

This request will work:

POST /connect/token HTTP/1.1
Host: localhost:44397
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=WebApplication4.Integration&client_secret=MySecretValue&scope=WebApplication4.ServerAPI

However I want this client to have its own AllowedScopes. If I then change AllowedScopes = { "WebApplication4.ServerAPI"} to AllowedScopes = { "WebApplication4.IntegrationAPI"} and of course modifying the request.

Server then responds with:

{
    "error": "invalid_scope"
}

If I look at logging I get the following history:

info: Duende.IdentityServer.Hosting.IdentityServerMiddleware[0]
      Invoking IdentityServer endpoint: Duende.IdentityServer.Endpoints.TokenEndpoint for /connect/token
info: Duende.IdentityServer.Events.DefaultEventService[0]
      {
        "ClientId": "WebApplication4.Integration",
        "AuthenticationMethod": "SharedSecret",
        "Category": "Authentication",
        "Name": "Client Authentication Success",
        "EventType": "Success",
        "Id": 1010,
        "ActivityId": "80000009-0014-d600-b63f-84710c7967bb",
        "TimeStamp": "2022-09-22T09:30:31Z",
        "ProcessId": 17768,
        "LocalIpAddress": "::1:44397",
        "RemoteIpAddress": "::1"
      }
fail: Duende.IdentityServer.Validation.DefaultResourceValidator[0]
      Scope WebApplication4.IntegrationAPI not found in store or not supported by requested resource indicators.
fail: Duende.IdentityServer.Validation.TokenRequestValidator[0]
      Invalid scopes requested, {
        "ClientId": "WebApplication4.Integration",
        "GrantType": "client_credentials",
        "AuthorizationCode": "********",
        "RefreshToken": "********",
        "Raw": {
          "grant_type": "client_credentials",
          "client_id": "WebApplication4.Integration",
          "client_secret": "***REDACTED***",
          "scope": "WebApplication4.IntegrationAPI"
        }
      }
info: Duende.IdentityServer.Events.DefaultEventService[0]
      {
        "ClientId": "WebApplication4.Integration",
        "Endpoint": "Token",
        "GrantType": "client_credentials",
        "Error": "invalid_scope",
        "Category": "Token",
        "Name": "Token Issued Failure",
        "EventType": "Failure",
        "Id": 2001,
        "ActivityId": "80000009-0014-d600-b63f-84710c7967bb",
        "TimeStamp": "2022-09-22T09:30:31Z",
        "ProcessId": 17768,
        "LocalIpAddress": "::1:44397",
        "RemoteIpAddress": "::1"
      }

What I take with me is this:

Scope WebApplication4.IntegrationAPI not found in store or not supported by requested resource indicators.

I then looked at these guides:

IdentityServer/IdentityServer4#4632 (comment)

https://docs.duendesoftware.com/identityserver/v5/quickstarts/1_client_credentials/

I therefore added this code:

public static class Config
{
    public static IEnumerable<ApiScope> ApiScopes =>
        new List<ApiScope>
        {
        new ApiScope("WebApplication4.IntegrationAPI", "Integration API")
        };
}

and

services.AddIdentityServer()
    .AddInMemoryApiScopes(Config.ApiScopes)
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>...

This still gave me the same error.

I then added a new list with clients:

public static IEnumerable<Duende.IdentityServer.Models.Client> Clients =>
    new List<Duende.IdentityServer.Models.Client>
    {
        new Duende.IdentityServer.Models.Client
        {
            ClientId = "WebApplication4.Integration",
            AllowedGrantTypes = { GrantType.ClientCredentials },
            ClientSecrets = { new Secret("MySecretValue".Sha256()) },
            AllowedScopes = { "WebApplication4.IntegrationAPI" },
        }
    };

Removed the old client from AddApiAuthorization and used this code instead:

services.AddIdentityServer()
    .AddInMemoryApiScopes(Config.ApiScopes)
    .AddInMemoryClients(Config.Clients)
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => ...

This gave me a new error when requesting a token:

{
    "error": "invalid_client"
}

Logs:

info: Duende.IdentityServer.Hosting.IdentityServerMiddleware[0]
      Invoking IdentityServer endpoint: Duende.IdentityServer.Endpoints.TokenEndpoint for /connect/token
info: Duende.IdentityServer.Events.DefaultEventService[0]
      {
        "ClientId": "WebApplication4.Integration",
        "Category": "Authentication",
        "Name": "Client Authentication Failure",
        "EventType": "Failure",
        "Id": 1011,
        "Message": "Unknown client",
        "ActivityId": "8000000a-0016-e700-b63f-84710c7967bb",
        "TimeStamp": "2022-09-22T09:54:08Z",
        "ProcessId": 10676,
        "LocalIpAddress": "::1:44397",
        "RemoteIpAddress": "::1"
      }
fail: Duende.IdentityServer.Validation.ClientSecretValidator[0]
      No client with id 'WebApplication4.Integration' found. aborting

If I look at https://localhost:44397/.well-known/openid-configuration I only see WebApplication4.ServerAPI in scopes_supported no matter the configuration.

Describe the solution you'd like

I want to do it like this so that I can add a policy like this later:

services.AddAuthorization(options =>
{
    options.AddPolicy("IntegrationApiScope", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("scope", "WebApplication4.IntegrationAPI");
    });
});

Meaning that I only want Client Credentials flow to get the scope WebApplication4.IntegrationAPI and I don't want Authorization Code Grant, normal login flow via authorization_code, to have this scope or be able to request this scope.

Additional context

No response

@TanayParikh TanayParikh added the area-identity Includes: Identity and providers label Sep 22, 2022
@blowdart blowdart added feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly External This is an issue in a component not contained in this repository. It is open for tracking purposes. labels Sep 23, 2022
@adityamandaleeka
Copy link
Member

@javiercn @SteveSandersonMS Can you please take a look at this?

@javiercn
Copy link
Member

javiercn commented Oct 3, 2022

@Ogglas thanks for contacting us.

Does the same configuration work without using our integration package?

@javiercn javiercn added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label Oct 3, 2022
@ghost
Copy link

ghost commented Oct 3, 2022

Hi @Ogglas. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@Ogglas
Copy link
Author

Ogglas commented Oct 5, 2022

@javiercn Yes it does. You can try it out by installing Duende IdentityServer Templates like this:

dotnet new --install Duende.IdentityServer.Templates

https://docs.duendesoftware.com/identityserver/v5/quickstarts/0_overview/

Then create a new project with template: "Duende IdentityServer with ASP.NET Core Identity (Duende Software)"

Works:

image

Does not work:

image

image

I did manage to get an extra scope like this:

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
        options.ApiScopes.Add(new ApiScope("scope1"));
        options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
        {
            builder.WithRedirectUri("/authentication/login-callback");
            builder.WithLogoutRedirectUri("/authentication/logout-callback");
        });
        options.Clients.Add(new Duende.IdentityServer.Models.Client
        {
            ClientId = "WebApplication4.Integration",
            AllowedGrantTypes = { GrantType.ClientCredentials },
            ClientSecrets = { new Secret("MySecretValue".Sha256()) },
            AllowedScopes = { "scope1" }
        });
    });

However WebApplication4.Integration can still request the scope WebApplication4.ServerAPI and get a token with it.

If I remove AddApiAuthorization I get this error:

No service for type 'Duende.IdentityServer.Stores.ISigningCredentialStore' has been registered.

I'm guessing it has something to do with AddApiAuthorization from NuGet Microsoft.AspNetCore.ApiAuthorization.IdentityServer.

https://github.com/dotnet/aspnetcore/blob/main/src/Identity/ApiAuthorization.IdentityServer/src/IdentityServerBuilderConfigurationExtensions.cs

Using the following code:

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
        options.ApiScopes.Add(new ApiScope("scope1"));
        options.ApiScopes.Add(new ApiScope("scope2"));
        options.Clients.Add(new Duende.IdentityServer.Models.Client
        {
            ClientId = "WebApplication4.Integration",
            AllowedGrantTypes = { GrantType.ClientCredentials },
            ClientSecrets = { new Secret("MySecretValue".Sha256()) },
            AllowedScopes = { "scope1" }
        });
    });

WebApplication4.Integration can get a token using scopes WebApplication4.ServerAPI and scope1 but not with scope2. Any idea on how to prevent WebApplication4.ServerAPI to be added to all AllowedScopes?

@ghost ghost added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Oct 5, 2022
@adityamandaleeka adityamandaleeka added this to the Backlog milestone Oct 7, 2022
@ghost
Copy link

ghost commented Oct 7, 2022

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@adityamandaleeka adityamandaleeka removed the Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. label Oct 7, 2022
@Ogglas
Copy link
Author

Ogglas commented Oct 13, 2022

@adityamandaleeka @javiercn @SteveSandersonMS I noticed something else as well.

Using the following code:

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
        options.ApiScopes.Add(new ApiScope("scope1"));
        options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
        {
            builder.WithRedirectUri("/authentication/login-callback");
            builder.WithLogoutRedirectUri("/authentication/logout-callback");
        });
        options.Clients.Add(new Duende.IdentityServer.Models.Client
        {
            ClientId = "WebApplication4.Integration",
            AllowedGrantTypes = { GrantType.ClientCredentials },
            ClientSecrets = { new Secret("MySecretValue".Sha256()) },
            AllowedScopes = { "scope1" }
        });
    });

I get a token with the following request:

POST /connect/token HTTP/1.1
Host: localhost:44397
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=WebApplication4.Integration&client_secret=MySecretValue&scope=scope1

I then tried to create a policy like this:

services.AddAuthorization(options =>
{
    options.AddPolicy("ApiScope", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("scope", "scope1");
    });
});

And use it like this:

[HttpGet]
[Authorize(Policy = "ApiScope")]
public ActionResult<IEnumerable<string>> Get()

Using the access_token I got from the request resulted in a 401 Unauthorized response. I then tried with simply [Authorize] but I still receive 401 Unauthorized. If I change RequireClaim to policy.RequireClaim("scope", "WebApplication4.ServerAPI"); and request a new token like above but with scope WebApplication4.ServerAPI instead of scope1. Then both [Authorize] and [Authorize(Policy = "ApiScope")] works as expected. Any idea how to get around this? Also if you have any idea on how to prevent WebApplication4.ServerAPI to be added to all AllowedScopes it would be very appreciated.

@marinasundstrom
Copy link

marinasundstrom commented Oct 18, 2022

My Blazor app stopped working with a "invalid_scope".

Source: https://github.com/marinasundstrom/todo-app

Here is the Identity Server log:

[identityservice_a6b737f7-2]: [13:37:21 Debug] Duende.IdentityServer.EntityFramework.Stores.ResourceStore
[identityservice_a6b737f7-2]: Found ["openid", "profile"] identity scopes in database
[identityservice_a6b737f7-2]:
[identityservice_a6b737f7-2]: [13:37:21 Debug] Duende.IdentityServer.EntityFramework.Stores.ResourceStore
[identityservice_a6b737f7-2]: Found ["myapi"] API resources in database
[identityservice_a6b737f7-2]:
[identityservice_a6b737f7-2]: [13:37:21 Debug] Duende.IdentityServer.EntityFramework.Stores.ResourceStore
[identityservice_a6b737f7-2]: Found ["myapi"] scopes in database
[identityservice_a6b737f7-2]:
[identityservice_a6b737f7-2]: [13:37:21 Error] Duende.IdentityServer.Validation.DefaultResourceValidator
[identityservice_a6b737f7-2]: Scope email not found in store or not supported by requested resource indicators.
[identityservice_a6b737f7-2]:
[identityservice_a6b737f7-2]: [13:37:21 Error] Duende.IdentityServer.Endpoints.AuthorizeEndpoint
[identityservice_a6b737f7-2]: Request validation failed

IdentityServer config:

    public static IEnumerable<ApiResource> ApiResources =>
        new ApiResource[]
        {
                // the api requires the role claim
                new ApiResource("myapi", "The Web Api", new[] { JwtClaimTypes.Name, JwtClaimTypes.PreferredUserName, JwtClaimTypes.Email, JwtClaimTypes.Role })
                {
                    Scopes = new string[] { "myapi" }
                }
        };

I started working again when I removed the scope "email" from "DefaultScopes" in appsettings.json in my Blazor app:

{
  "Local": {
    "Authority": "https://localhost:5041",
    "ClientId": "clientapp",
    "RedirectUri": "https://localhost:5021/authentication/login-callback",
    "ResponseType": "code",
    "DefaultScopes": [
      "openid",
      "profile",
      // "email",
      "myapi"
    ],
    "PostLogoutRedirectUri": "https://localhost:5021/"
  }
}

@leastprivilege
Copy link
Contributor

I recommend

https://docs.duendesoftware.com/identityserver/v6/upgrades/spa_to_duende/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-identity Includes: Identity and providers External This is an issue in a component not contained in this repository. It is open for tracking purposes. feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly
Projects
None yet
Development

No branches or pull requests

8 participants
@adityamandaleeka @blowdart @marinasundstrom @leastprivilege @Ogglas @javiercn @TanayParikh and others