title: OpenID Connect AllowedRedirectUris empty resulting in "invalid redirect_uri" on IdentityServer4
body: <p>I have a running IdentityServer4 on a .NET Core project, and I'm trying to make an existing .NET Framework web application a client of that server.</p>
  
  <p>The project is using <code>Microsoft.Owin.Security</code>, and in the <code>Startup.cs</code> I setup the authentication settings.</p>
  
  <p>However, when the request is sent to the IdentityServer, the <code>AllowedRedirectUris</code> parameters is just an empty array, so the <code>RedirectUri</code> parameter doesn't match one in that empty array, resulting in <code>invalid redirect_uri</code> error.</p>
  
  <p>Here's the <code>Startup.cs</code> of the existing project:</p>
  
  <pre><code>using Microsoft.Owin.Security;
  using Microsoft.Owin.Security.Cookies;
  using Microsoft.Owin.Security.OpenIdConnect;
  using Owin;
  
  namespace MyProject.Services
  {
      public partial class Startup
      {
          public void ConfigureAuth(IAppBuilder app)
          {
              app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
  
              app.UseCookieAuthentication(new CookieAuthenticationOptions {
                  AuthenticationType = "Cookies"
              });
  
              app.UseOpenIdConnectAuthentication(
                  new OpenIdConnectAuthenticationOptions
                  {
                      RequireHttpsMetadata = false,
                      AuthenticationType = "oidc",
                      SignInAsAuthenticationType = "Cookies",
                      Authority = "http://localhost:5000",
                      RedirectUri = "http://localhost:8010/myproject.services/api/oidc",
                      PostLogoutRedirectUri = "http://localhost:8010/myproject.application",
                      ClientId = "CLIENT1",
                      ClientSecret = "a-local-testing-password",
                      Scope = "CLIENT1 offline_access",
                      ResponseType = "code id_token" 
                  });
          }
      }
  }
  </code></pre>
  
  <p>Here's the generated request from that configuration that was found in the logging:</p>
  
  <pre><code>IdentityServer4.Endpoints.AuthorizeEndpoint[0]
  {
    "ClientId": "CLIENT1",
    "ClientName": "CLIENT1_name",
    "RedirectUri": "http://localhost:8010/myproject.services/api/oidc",
    "AllowedRedirectUris": [], &lt;-- is empty, but should contain the RedirectUri
    "SubjectId": "anonymous",
    "RequestedScopes": "",
    "Raw": {
      "client_id": "CLIENT1",
      "redirect_uri": "http://localhost:8010/myproject.services/api/oidc",
      "response_mode": "form_post",
      "response_type": "code id_token",
      "scope": "openid profile",
      "state": "OpenIdConnect.AuthenticationProperties=[...]",
      "nonce": "[...]",
      "x-client-SKU": "ID_NET451",
      "x-client-ver": "5.2.1.0"
    }
  }
  </code></pre>
  
  <p>So does anybody know how I can make the <code>AllowedRedirectUris</code> contain the given<code>RedirectUri</code>so that my IdentityServer4 would accept the request?</p>
  
  <p><strong>UPDATE</strong>: Here's the client object in the IdentityServer4 project:</p>
  
  <pre><code>                new IdentityServer4.Models.Client
                  {
                      ClientId = "CLIENT1",
                      ClientName = "CLIENT1-name",
                      AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                      ClientSecrets =
                      {
                          new IdentityServer4.Models.Secret("a-local-testing-password".Sha256())
                      },
                      RedirectUris = { "http://localhost:8010/myproject.services/api/oidc" },
                      PostLogoutRedirectUris = { "http://localhost:8010/myproject.application" },
                      AlwaysSendClientClaims = true,
                      AlwaysIncludeUserClaimsInIdToken = true,
                      AllowedScopes = {
                          "CLIENT1"
                      },
                      Properties = new Dictionary&lt;string, string&gt;
                      {
                          { "TenantName", "CLIENT1" },
                          { "ImageUrl", "/Images/CLIENT1.png" },
                      },
                      IdentityProviderRestrictions = {
                          "LocalOnly"
                      },
                      AllowOfflineAccess = true
                  },
  </code></pre>
  
  <p><strong>Update2</strong>: Here's the <code>Startup.cs</code> of the IdentityServer4:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;
  using Microsoft.AspNetCore.Builder;
  using Microsoft.AspNetCore.Identity;
  using Microsoft.EntityFrameworkCore;
  using Microsoft.AspNetCore.Hosting;
  using Microsoft.Extensions.Configuration;
  using Microsoft.Extensions.DependencyInjection;
  using MyProject.STS4.ManagementSite.Data;
  using MyProject.STS4.ManagementSite.Models;
  using MyProject.STS4.ManagementSite.Services;
  using MyProject.STS4.ManagementSite.Tenant;
  using IdentityServer4.Services;
  using Microsoft.Extensions.DependencyInjection.Extensions;
  using System.Reflection;
  using IdentityServer4.EntityFramework.DbContexts;
  using IdentityServer4.EntityFramework.Mappers;
  using Microsoft.IdentityModel.Tokens;
  using IdentityServer4;
  using Microsoft.AspNetCore.Authentication.OpenIdConnect;
  using Microsoft.IdentityModel.Protocols.OpenIdConnect;
  using System.Globalization;
  using System.Security.Claims;
  using MyProject.STS4.ManagementSite.Models.Mail;
  
  namespace MyProject.STS4.ManagementSite
  {
      public class Startup
      {
          public Startup(IConfiguration configuration)
          {
              Configuration = configuration;
          }
  
          public IConfiguration Configuration { get; }
  
          // This method gets called by the runtime. Use this method to add services to the container.
          public void ConfigureServices(IServiceCollection services)
          {
              // Read this part of the appsettings.json and convert it into the SmtpConfig
              services.Configure&lt;SmtpConfig&gt;(Configuration.GetSection("Smtp"));
  
              string connectionString = Configuration.GetConnectionString("DefaultConnection");
              var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
  
              services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
                   options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                   sqlOptions =&gt; sqlOptions.MigrationsAssembly(migrationsAssembly)
                   ));
  
              // Explicitly added
              //services.TryAddScoped&lt;IUserClaimsPrincipalFactory&lt;TenantUser&gt;, UserClaimsPrincipalFactory&lt;TenantUser, IdentityRole&gt;&gt;();
  
              services.AddIdentity&lt;TenantUser, IdentityRole&gt;(options =&gt;
              {
                  // This should send a mail when registering
                  options.SignIn.RequireConfirmedEmail = false;
  
                  options.User.RequireUniqueEmail = false;
                  // Allows emails toe be used in the username
                  options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
              })
                .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()
                .AddDefaultTokenProviders()
                .AddUserValidator&lt;TenantUserValidator&gt;(); // register our own validator
  
              // Add application services.
              services.AddTransient&lt;IEmailSender, EmailSender&gt;();
  
              services.AddMvc();
  
              services.AddIdentityServer()
                  .AddDeveloperSigningCredential()
                  .AddAspNetIdentity&lt;TenantUser&gt;()
                  // this adds the config data from DB (clients, resources)
                  .AddConfigurationStore(options =&gt;
                  {
                      options.ConfigureDbContext = builder =&gt;
                          builder.UseSqlServer(connectionString,
                              sql =&gt; sql.MigrationsAssembly(migrationsAssembly));
                  })
                  // this adds the operational data from DB (codes, tokens, consents)
                  .AddOperationalStore(options =&gt;
                  {
                      options.ConfigureDbContext = builder =&gt;
                          builder.UseSqlServer(connectionString,
                              sql =&gt; sql.MigrationsAssembly(migrationsAssembly));
  
                      // this enables automatic token cleanup. this is optional.
                      options.EnableTokenCleanup = true;
                      options.TokenCleanupInterval = 30;
                  });
  
              // Add the possible authentication methods
              services.AddAuthentication()
                  .AddGoogle("Google", options =&gt;
                  {
                      options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
  
                      options.ClientId = "[...]";
                      options.ClientSecret = "[...]";
                  })
                  .AddOpenIdConnect("oidc", "OpenID Connect", options =&gt;
                  {
                      options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                      options.SignOutScheme = IdentityServerConstants.SignoutScheme;
  
                      options.Authority = "http://localhost:5000/";
                      options.RequireHttpsMetadata = false;
                      options.ClientId = "implicit";
  
                      options.TokenValidationParameters = new TokenValidationParameters
                      {
                          NameClaimType = "name",
                          RoleClaimType = "role"
                      };
                  });
  
              // This is still to test Azure Auth
              //.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
              //{
              //  ClientId = Configuration["AzureAD:ClientId"],
              //  Authority = string.Format(CultureInfo.InvariantCulture, Configuration["AzureAd:AadInstance"], "common", "/v2.0"),
              //  ResponseType = OpenIdConnectResponseType.IdToken,
  
              //  Events = new OpenIdConnectEvents
              //  {                       
              //      OnTokenValidated = TokenValidated
              //  },
              //  TokenValidationParameters = new TokenValidationParameters
              //  {
              //      // Instead of using the default validation (validating against
              //      // a single issuer value, as we do in line of business apps), 
              //      // we inject our own multitenant validation logic
              //      ValidateIssuer = false,
  
              //      NameClaimType = "name"
              //  }
              //}
              //);
  
              // Registering this service allows for checking if user is active and enhancing the user with extra data (such as the claims)
              services.AddTransient&lt;IProfileService, TenantUserProfileService&gt;();
          }
  
          private Task TokenValidated(TokenValidatedContext context)
          {
              /* ---------------------
              // Replace this with your logic to validate the issuer/tenant
                  ---------------------       
              // Retriever caller data from the incoming principal
              string issuer = context.SecurityToken.Issuer;
              string subject = context.SecurityToken.Subject;
              string tenantID = context.Ticket.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
  
              // Build a dictionary of approved tenants
              IEnumerable&lt;string&gt; approvedTenantIds = new List&lt;string&gt;
              {
                  "&lt;Your tenantID&gt;",
                  "9188040d-6c67-4c5b-b112-36a304b66dad" // MSA Tenant
              };
              o
              if (!approvedTenantIds.Contains(tenantID))
                  throw new SecurityTokenValidationException();
                  --------------------- */
  
              var claimsIdentity = (ClaimsIdentity)context.Principal.Identity;
              //add your custom claims here
              claimsIdentity.AddClaim(new Claim("test", "helloworld!!!"));
  
              return Task.FromResult(0);
          }
  
          // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
          {
              // this will do the initial DB population
              SeedData.EnsureSeedData(app.ApplicationServices);
  
              if (env.IsDevelopment())
              {
                  app.UseDeveloperExceptionPage();
                  app.UseBrowserLink();
                  app.UseDatabaseErrorPage();
              }
              else
              {
                  app.UseExceptionHandler("/Home/Error");
              }
  
              app.UseStaticFiles();
  
              // app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware
              app.UseIdentityServer();
  
              app.UseMvc(routes =&gt;
              {
                  routes.MapRoute(
                      name: "default",
                      template: "{controller=Home}/{action=Index}/{id?}");
              });
          }
      }
  }
  </code></pre>