Search code examples
c#authenticationoauth-2.0identityserver4openid-connect

Client redirect does not work after login due to Bad Request error


I am working on a project that uses login via a IdentityServer 4 authentication server that uses. We have multiple seperately hosted client applications that will access this.

We pull in the allowed clients from a database connected to the Authentication server, and each client will have the ClientID/Secret injected into the application.

Client Auth Configuration

public static void Configure(this WebApplicationBuilder builder)
{
    builder.SetEnvironment();
    builder.ConfigureClientDetails();

    ConfigureAuthServer(builder);
    //...
    var anonymousPages = new string[] { "/", "/index", "/about" };
    var authPages = new string[] { "/welcome" };
    app.AddAuthorizationControl(anonymousPages, authPages);

    app.UseAuthorization();
    //...
    app.Run();
}


private static void ConfigureAuthServer(WebApplicationBuilder builder)
{
    builder.Services.AddAccessTokenManagement();

    string[] scopes = { 
        "odata-api", 
        "openid",
        "name", 
        "role", 
        "profile", 
        "offline_access" 
    };

    builder.Services.AddAuthClient(AppEnvironment.AuthUrl, AppClient.ClientName, 
        AppClient.ClientID, AppClient.ClientSecret, scopes);
}

Authentication Service Adapter (Git Submodule connection)

public static void AddAuthClient(this IServiceCollection services, string authServerUrl,
    string clientUrl, string clientId, string clientSecret, string[] scopes)
{
    Configuration.CookieName = "Cookies";
    Configuration.AuthorityUrl = authServerUrl;
    Configuration.ClientUrl = clientUrl;
    Configuration.ClientId = new Guid(clientId);
    Configuration.ClientSecret = clientSecret;

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    services.AddHttpClient();
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = Configuration.CookieName;
        options.DefaultChallengeScheme = AUTHENTICATION_SCHEME;
    })
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.Authority = authServerUrl;
        options.TokenValidationParameters.ValidateAudience = false;
        options.TokenValidationParameters.ValidTypes = new[] { "at+jwt" };
    })
    .AddCookie(Configuration.CookieName, options =>
    {
        options.ExpireTimeSpan = TimeSpan.FromHours(1);
        options.SlidingExpiration = true;
    })
    .AddOpenIdConnect(AUTHENTICATION_SCHEME, options =>
    {
        options.SignInScheme = Configuration.CookieName;
        options.Authority = Configuration.AuthorityUrl;
        options.RequireHttpsMetadata = false;

        options.ClientId = clientId;
        options.ClientSecret = clientSecret;
        options.SaveTokens = true; 
        options.ClaimActions.MapAll();

        options.GetClaimsFromUserInfoEndpoint = true;
        options.ResponseType = "code";
        options.ResponseMode = "query";
        options.Scope.Clear();

        foreach (var scope in scopes)
        {
            options.Scope.Add(scope);
        }

        options.ClaimActions.Add(new JsonKeyClaimAction("name", "name", "name"));
        options.ClaimActions.Add(new JsonKeyClaimAction("role", "role", "role"));

        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "name",
            RoleClaimType = "role"
        };

        options.Events = new OpenIdConnectEvents
        {
            // ... Standard events with await Task.Yield()
        }
}

To run this, I have both solutions running at the same time.

Auth: https://localhost:5000

Client: https://localhost:7300

When I click the login button on the client, it successfully redirects to the auth server login. Once credentials are entered and submitted, a token is successfully issued and login to the authentication server is successful.

However the redirection to the client main page fails and an exception is thrown in the RedeemAuthorizationCodeAsync() method within the Microsoft.AspNetCore.Authentication.OpenIdConnect OpenIdConnectHandler due to the response message being sent to the Backchannel via Backchannel.SendAsync returning a 400 Bad Request so the user is never authenticated for the client, but it goes through fully for the Authentication server and you can perform normal actions there.

Internal Server Error Page

We have the authentication server published remotely and it works fine there, but for local connections for development and testing, it does not work. I have tried adjusting the client configuration, as well as trying against the remote server but the same error persists.


Solution

  • On looking into this further by going through breakpoints and checking logs, it turns out the errors are being thrown due to the validation of the client secret value.