Search code examples
asp.net-coreidentityserver4blazor

Identityserver4 not authorized api in blazor client


I use identity server 4 with blazor server side client

everything is ok but token not authorized api methods but token works in server authorized controllers

is something wrong with grant type or code flow ?

server config class :

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

        public static IEnumerable<ApiResource> GetApis() =>
            new List<ApiResource> {
                new ApiResource("api1")
            };

        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new List<ApiScope>
            {
                // backward compat
                new ApiScope("api1")
            };
        }

        public static IEnumerable<Client> GetClients() => new List<Client>
        {
        new Client
            {
                ClientId = "client",
                AllowedGrantTypes = GrantTypes.Code,
                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },
                AllowedScopes = { 
                        "api1" ,
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                },
                RedirectUris = { "https://localhost:44372/signin-oidc" },

                AlwaysIncludeUserClaimsInIdToken = true,

                AllowOfflineAccess = true,
                RequireConsent = false,
                RequirePkce = true,
            }
        };

     
    }

server start up class :

  public void ConfigureServices(IServiceCollection services)
        {

            services.AddDbContext<AppDbContext>(config =>
            {
                config.UseInMemoryDatabase("Memory");
            });

            // AddIdentity registers the services
            services.AddIdentity<IdentityUser, IdentityRole>(config =>
            {
                config.Password.RequiredLength = 4;
                config.Password.RequireDigit = false;
                config.Password.RequireNonAlphanumeric = false;
                config.Password.RequireUppercase = false;
            })
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

            services.ConfigureApplicationCookie(config =>
            {
                config.Cookie.Name = "IdentityServer.Cookie";
                config.LoginPath = "/Auth/Login";
                config.LogoutPath = "/Auth/Logout";
            });



            services.AddIdentityServer()
                .AddAspNetIdentity<IdentityUser>()
               //.AddInMemoryApiResources(Configurations.GetApis())
               .AddInMemoryIdentityResources(Configurations.GetIdentityResources())
               .AddInMemoryApiScopes(Configurations.GetApiScopes())
               .AddInMemoryClients(Configurations.GetClients())
               .AddDeveloperSigningCredential();

            services.AddControllersWithViews();
        }

api start up class :

            services.AddAuthentication("Bearer").AddIdentityServerAuthentication(option =>
            {
                option.Authority = "https://localhost:44313";
                option.RequireHttpsMetadata = false;
                option.ApiName = "api1";
            });

blazor server side start up class:

services.AddAuthentication(config =>
            {
                config.DefaultScheme = "Cookie";
                config.DefaultChallengeScheme = "oidc";
            })
                .AddCookie("Cookie")
                .AddOpenIdConnect("oidc", config =>
                {
                    config.Authority = "https://localhost:44313/";
                    config.ClientId = "client";
                    config.ClientSecret = "secret";
                    config.SaveTokens = true;
                    config.ResponseType = "code";
                    config.SignedOutCallbackPath = "/";
                    config.Scope.Add("openid");
                    config.Scope.Add("api1");
                    config.Scope.Add("offline_access");
                });

            services.AddMvcCore(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser() // site-wide auth
                    .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            });

Solution

  • To fix this issue, you have 2 options:

    1- (Recommended) To add the scopes to API resource like this:

    public static IEnumerable<ApiResource> GetApis() =>
                new List<ApiResource> {
                    new ApiResource("api1")
                    {
                        Scopes = new []{ "api1" }
                    }
                };
    
            public static IEnumerable<ApiScope> GetApiScopes()
            {
                return new List<ApiScope>
                {
                    // backward compat
                    new ApiScope("api1")
                };
            }
    
    

    2- On API change your code to set ValidateAudience = false, like this:

    services.AddAuthentication("Bearer").AddJwtBearer("Bearer",
       options =>
       {
          options.Authority = "http://localhost:5000";
          options.Audience = "api1";
          options.RequireHttpsMetadata = false;
          options.TokenValidationParameters = new 
             TokenValidationParameters()
             {
                ValidateAudience = false
             };
       });
    

    Here is my blog about migrating IdentityServer4 to v4 https://nahidfa.com/posts/migrating-identityserver4-to-v4/