1
votes

Yesterday i have updated Identity server 4 to version 2.4.0 - the package also include one migration that has to be executed and applied to Indentity database (it added some columns to Client table and e.t). After the update of the version and deployment of to our dev server when request is perform durring the redirection to signin?ReturnUrl=/connect Identity server return 404. Front end is using oidc-client to comunicated with Indentity server. I want to point that before upgrading to 2.4.0 everything work as expected. If i nagivate manually to login page of Indentity server im able to load it correctly

I cannot find what goes wrong after the update of this package and i start to get feeling that is related to this migration that i executed its added default values to newly added columns and maybe some of them are not included to oidc-client

Please help me this drive me crazy

oidc-client settings

  const settings: UserManagerSettings = {
  authority: 'https://login.' + appConfig.config.mainDomain + '/',
  client_id: environment.clientId,
  client_secret: environment.clientSecret,
  redirect_uri: window.location.origin + '/auth-callback',
  post_logout_redirect_uri: window.location.origin + '/',

  // these two will be done dynamically from the buttons clicked, but are
  // needed if you want to use the silent_renew
  response_type: 'id_token token',
  scope: 'openid profile address test',

  // this will toggle if profile endpoint is used
  loadUserInfo: true,
  // silent renew will get a new access_token via an iframe
  // just prior to the old access_token expiring (60 seconds prior)
  silent_redirect_uri: window.location.origin + '/assets/silent.html',
  automaticSilentRenew: true,
  // will revoke (reference) access tokens at logout time
  revokeAccessTokenOnSignout: true,
  // this will allow all the OIDC protocol claims to be visible in the window. normally a client app
  // wouldn't care about them or want them taking up space
  filterProtocolClaims: false
};

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        try
        {
            services.AddCors(options =>
            {
                // this defines a CORS policy called "default"
                options.AddPolicy("default", policy =>
                {
                    policy.AllowAnyOrigin()
                        .AllowAnyHeader()
                        .AllowAnyMethod();
                });
            });
            if (windowsProvider != null && Convert.ToBoolean(windowsProvider[Constants.AuthenticationEnabled]))
            {
                services.Configure<IISOptions>(iis =>
                {
                    iis.AuthenticationDisplayName = windowsProvider[Constants.AuthenticationDisplayName];
                    iis.AutomaticAuthentication = false;
                });
            }

            var identityConnection = _mm.ConfManager.GetSection(Constants.DbConnections)[Constants.IdentityServerData];
            var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
            services.AddMvc().AddMvcOptions(o => { o.Filters.Add(new GlobalExceptionFilter(_mm)); });
            services.AddIdentityServer(x => { x.PublicOrigin = _mm.ConfManager[Constants.PublicOrigin]; })
                .AddSigningCredential(Certificates.LoadCertificateFromStore(_mm.ConfManager[Constants.Certificate]))
                .AddBgoIdpUserStore()
                .AddRedirectUriValidator<AnyRedirectUriValidator>()
                .AddConfigurationStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(identityConnection, sql => sql.MigrationsAssembly(migrationsAssembly)); })
                .AddOperationalStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(identityConnection, sql => sql.MigrationsAssembly(migrationsAssembly)); });

            services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; })
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
                {
                    options.LoginPath = new PathString(Constants.SignIn);
                    options.LogoutPath = new PathString(Constants.SignOut);
                    options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
                });



            services.AddLocalApiAuthentication();
        }
        catch (Exception ex)
        {
            _mm.LogErrMessage(ex.Message);
            _mm.LogTraceException(ex, ex.Message);
        }
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IDPUserContext idpUserContext, ConfigurationDbContext configurationDbContext, PersistedGrantDbContext persistedGrantDbContext)
    {
        try
        {


            // global policy - assign here or on each controller
            app.UseCors("default");
            app.UseExceptionHandler(Constants.ExceptionHandler);

            app.Use(async (context, next) =>
            {
                if (context.Request.QueryString.HasValue)
                {
                    if (!context.Request.Headers.ContainsKey("Authorization"))
                    {
                        var queryString = HttpUtility.ParseQueryString(context.Request.QueryString.Value);
                        string token = queryString.Get("access_token");

                        if (!string.IsNullOrWhiteSpace(token))
                        {
                            context.Request.Headers.Add("Authorization", new[] { string.Format("Bearer {0}", token) });
                        }
                    }
                }

                await next.Invoke();
            });


            app.UseIdentityServer();
            app.UseAuthentication();
            app.UseStaticFiles();
            app.UseMiddleware(typeof(ErrorHandlingMiddleware));
            app.UseMvcWithDefaultRoute();

        }

Here is the GetClients()

public static IEnumerable < Client > GetClients() {
return new List < Client > () {

    new Client {
        ClientName = "JS dev client",
            ClientId = "JSDevClient",
            AllowedCorsOrigins = {
                "http://mfm.localhost",
                "https://mfm.localhost",
                "https://test.test.app",
                "http://test.test.app",
                "https://localhost:4200",
                "http://localhost:4200",
                "https://test.app",
                "https://*.test.app"
            },
            AccessTokenType = AccessTokenType.Reference,

            //TODO: Not sure if we need the second screen ,check with the business
            RequireConsent = false, // setting usage or not for consent screen

            AccessTokenLifetime = 1200,
            AuthorizationCodeLifetime = 1200,
            IdentityTokenLifetime = 1200,
            UpdateAccessTokenClaimsOnRefresh = true,
            AllowOfflineAccess = true,
            AllowAccessTokensViaBrowser = true,

            ClientSecrets = {
                new Secret("jsdevclient;dorgfgitvzmoygksqjpm,".Sha256())
            },
            AllowedGrantTypes = GrantTypes.Implicit,
            RedirectUris = new List < string > {
                "http://mfm.localhost/callback.html",
                "http://mfm.localhost/auth-callback",
                "https://mfm.localhost/auth-callback",
                "https://test.test.app/assets/silent.html",
                "http://test.test.app/assets/silent.html",
                "https://test.test.app/auth-callback",
                "http://test.test.app/auth-callback",
                "https://*.test.app/assets/silent.html",
                "https://*.test.app/auth-callback",
                "http://localhost:4200/auth-callback",
                "https://localhost:4200/auth-callback",
                "http://localhost:4200/assets/silent.html",
                "https://localhost:4200/assets/silent.html"
            },
            AllowedScopes = {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.Address,
                "testapi",
            },
            PostLogoutRedirectUris = {
                "http://mfm.localhost/index.html",
                "http://mfm.localhost/",
                "https://mfm.localhost/",
                "https://dev-test.metaforms.app",
                "http://dev-test.metaforms.app",
                "http://localhost:4200",
                "https://localhost:4200",
                "https://test.app",
                "https://test.app",
                "https://*.test.app",
                "https://test.metaforms.app",
                "http://test.metaforms.app"
            },
            AlwaysIncludeUserClaimsInIdToken = true,
    },
};

}

Applied migration

protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<DateTime>(
            name: "Created",
            table: "IdentityResources",
            nullable: false,
            defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));

        migrationBuilder.AddColumn<bool>(
            name: "NonEditable",
            table: "IdentityResources",
            nullable: false,
            defaultValue: false);

        migrationBuilder.AddColumn<DateTime>(
            name: "Updated",
            table: "IdentityResources",
            nullable: true);

        migrationBuilder.AlterColumn<string>(
            name: "Value",
            table: "ClientSecrets",
            maxLength: 4000,
            nullable: false,
            oldClrType: typeof(string),
            oldMaxLength: 2000);

        migrationBuilder.AlterColumn<string>(
            name: "Type",
            table: "ClientSecrets",
            maxLength: 250,
            nullable: false,
            oldClrType: typeof(string),
            oldMaxLength: 250,
            oldNullable: true);

        migrationBuilder.AddColumn<DateTime>(
            name: "Created",
            table: "ClientSecrets",
            nullable: false,
            defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));

        migrationBuilder.AddColumn<DateTime>(
            name: "Created",
            table: "Clients",
            nullable: false,
            defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));

        migrationBuilder.AddColumn<int>(
            name: "DeviceCodeLifetime",
            table: "Clients",
            nullable: false,
            defaultValue: 0);

        migrationBuilder.AddColumn<DateTime>(
            name: "LastAccessed",
            table: "Clients",
            nullable: true);

        migrationBuilder.AddColumn<bool>(
            name: "NonEditable",
            table: "Clients",
            nullable: false,
            defaultValue: false);

        migrationBuilder.AddColumn<DateTime>(
            name: "Updated",
            table: "Clients",
            nullable: true);

        migrationBuilder.AddColumn<string>(
            name: "UserCodeType",
            table: "Clients",
            maxLength: 100,
            nullable: true);

        migrationBuilder.AddColumn<int>(
            name: "UserSsoLifetime",
            table: "Clients",
            nullable: true);

        migrationBuilder.AlterColumn<string>(
            name: "Value",
            table: "ApiSecrets",
            maxLength: 4000,
            nullable: false,
            oldClrType: typeof(string),
            oldMaxLength: 2000,
            oldNullable: true);

        migrationBuilder.AlterColumn<string>(
            name: "Type",
            table: "ApiSecrets",
            maxLength: 250,
            nullable: false,
            oldClrType: typeof(string),
            oldMaxLength: 250,
            oldNullable: true);

        migrationBuilder.AddColumn<DateTime>(
            name: "Created",
            table: "ApiSecrets",
            nullable: false,
            defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));

        migrationBuilder.AddColumn<DateTime>(
            name: "Created",
            table: "ApiResources",
            nullable: false,
            defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));

        migrationBuilder.AddColumn<DateTime>(
            name: "LastAccessed",
            table: "ApiResources",
            nullable: true);

        migrationBuilder.AddColumn<bool>(
            name: "NonEditable",
            table: "ApiResources",
            nullable: false,
            defaultValue: false);

        migrationBuilder.AddColumn<DateTime>(
            name: "Updated",
            table: "ApiResources",
            nullable: true);

        migrationBuilder.CreateTable(
            name: "ApiProperties",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                Key = table.Column<string>(maxLength: 250, nullable: false),
                Value = table.Column<string>(maxLength: 2000, nullable: false),
                ApiResourceId = table.Column<int>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_ApiProperties", x => x.Id);
                table.ForeignKey(
                    name: "FK_ApiProperties_ApiResources_ApiResourceId",
                    column: x => x.ApiResourceId,
                    principalTable: "ApiResources",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            });

        migrationBuilder.CreateTable(
            name: "IdentityProperties",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                Key = table.Column<string>(maxLength: 250, nullable: false),
                Value = table.Column<string>(maxLength: 2000, nullable: false),
                IdentityResourceId = table.Column<int>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_IdentityProperties", x => x.Id);
                table.ForeignKey(
                    name: "FK_IdentityProperties_IdentityResources_IdentityResourceId",
                    column: x => x.IdentityResourceId,
                    principalTable: "IdentityResources",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            });

        migrationBuilder.CreateIndex(
            name: "IX_ApiProperties_ApiResourceId",
            table: "ApiProperties",
            column: "ApiResourceId");

        migrationBuilder.CreateIndex(
            name: "IX_IdentityProperties_IdentityResourceId",
            table: "IdentityProperties",
            column: "IdentityResourceId");
    }

this is from logs

2019-08-29 10:43:47.300 +03:00 [WRN] App is started
2019-08-29 10:43:47.577 +03:00 [DBG] Login Url: /signin
2019-08-29 10:43:47.579 +03:00 [DBG] Login Return Url Parameter: ReturnUrl
2019-08-29 10:43:47.580 +03:00 [DBG] Logout Url: /signout
2019-08-29 10:43:47.580 +03:00 [DBG] ConsentUrl Url: /consent
2019-08-29 10:43:47.580 +03:00 [DBG] Consent Return Url Parameter: returnUrl
2019-08-29 10:43:47.580 +03:00 [DBG] Error Url: /home/error
2019-08-29 10:43:47.580 +03:00 [DBG] Error Id Parameter: errorId
2019-08-29 10:44:26.740 +03:00 [DBG] CORS request made for path: /.well-known/openid-configuration from origin: https://test.test.app
2019-08-29 10:44:26.767 +03:00 [DBG] Origin https://test.test.app is allowed: true
2019-08-29 10:44:26.773 +03:00 [DBG] CorsPolicyService allowed origin: https://test.test.app
2019-08-29 10:44:26.774 +03:00 [DBG] Request path /.well-known/openid-configuration matched to endpoint type Discovery
2019-08-29 10:44:26.790 +03:00 [DBG] Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint
2019-08-29 10:44:26.790 +03:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration
2019-08-29 10:44:26.808 +03:00 [DBG] Start discovery request
2019-08-29 10:44:27.756 +03:00 [DBG] Found openid, profile, address, roles, country, subscriptionlevel, metaformsmanagerapi, metaformsbrowserapi, metaformsnavigatorapi, emailprocessorapi, servicebusapi, filestoreapi, jstest, metaformsfrontend, IdentityServerApi as all scopes in database
2019-08-29 10:44:27.856 +03:00 [DBG] Request path /connect/authorize matched to endpoint type Authorize
2019-08-29 10:44:27.861 +03:00 [DBG] Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeEndpoint
2019-08-29 10:44:27.861 +03:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
2019-08-29 10:44:27.865 +03:00 [DBG] Start authorize request
2019-08-29 10:44:27.883 +03:00 [DBG] No user present in authorize request
2019-08-29 10:44:27.890 +03:00 [DBG] Start authorize request protocol validation
2019-08-29 10:44:28.224 +03:00 [DBG] JSDevClient found in database: true
2019-08-29 10:44:28.232 +03:00 [DBG] client configuration validation for client JSDevClient succeeded.
2019-08-29 10:44:28.300 +03:00 [DBG] Found openid, profile, address identity scopes in database
2019-08-29 10:44:28.353 +03:00 [DBG] Found test API scopes in database
2019-08-29 10:44:28.366 +03:00 [DBG] Found openid, profile, address identity scopes in database
2019-08-29 10:44:28.370 +03:00 [DBG] Found metaformsmanagerapi API scopes in database
2019-08-29 10:44:28.380 +03:00 [DBG] Calling into custom validator: IdentityServer4.Validation.DefaultCustomAuthorizeRequestValidator
2019-08-29 10:44:28.432 +03:00 [INF] ValidatedAuthorizeRequest
{
"ClientId": "JSDevClient",
"ClientName": "JS dev client",
"RedirectUri": "https://test.test.app/auth-callback",
"AllowedRedirectUris": ["https://*.test.app/assets/silent.html", "http://mfm.localhost/callback.html", "http://mfm.localhost/auth-callback", "https://mfm.localhost/auth-callback", "https://test.test.app/assets/silent.html", "http://test.test.app/assets/silent.html", "https://test.test.app/auth-callback", "http://test.test.app/auth-callback", "https://*.test.app/auth-callback", "http://localhost:4200/auth-callback", "https://localhost:4200/auth-callback", "http://localhost:4200/assets/silent.html", "https://localhost:4200/assets/silent.html"],
"SubjectId": "anonymous",
"ResponseType": "id_token token",
"ResponseMode": "fragment",
"GrantType": "implicit",
"RequestedScopes": "openid profile address testapi",
"State": "dffeb4fca72e43afaecb5fb5daff2348",
"UiLocales": null,
"Nonce": "8dad56338d7f40b595a9f2d91aba18d0",
"AuthenticationContextReferenceClasses": null,
"DisplayMode": null,
"PromptMode": null,
"MaxAge": null,
"LoginHint": null,
"SessionId": null,
"Raw": {
    "client_id": "JSDevClient",
    "redirect_uri": "https://test.test.app/auth-callback",
    "response_type": "id_token token",
    "scope": "openid profile address testapi",
    "state": "dffeb4fca72e43afaecb5fb5daff2348",
    "nonce": "8dad56338d7f40b595a9f2d91aba18d0"
},
"$type": "AuthorizeRequestValidationLog"
}
 2019-08-29 10:44:28.497 +03:00 [INF] Showing login: User is not authenticated
 2019-08-29 10:44:28.499+03:00 [INF] Cookies was not authenticated. Failure message: Unprotect ticket failed
 2019-08-29 10:44:28.499 +03:00 [INF] Cookies was not authenticated. Failure message: Unprotect ticket failed
 2019-08-29 10:44:28.499 +03:00 [INF] Cookies was not authenticated. Failure message: Unprotect ticket failed
1
I have revert IdentityServer4 to 2.2.0 redirect work correctly then update to 2.3.0 with IdentityServer4.EntityFramework package also.There are not changes over the code before update to 2.3.0 just executing the migration with changes that was included into 2.3.0(the database model only contains the changes that was included in system migration included into 2.3.0).After migration was applyed over my local database - redirect to identity server to signin?ReturnUrl=/connect from oidc-client stop work.I can confrim that this migration cause the problem with 404 from oidc-client. - NDym

1 Answers

0
votes

I found what cause the problem but i dont know why

 services.AddAuthentication(options => { options.DefaultScheme = 
    CookieAuthenticationDefaults.AuthenticationScheme; })
   .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
    {
     //options.LoginPath = new PathString(Constants.SignIn);
     //options.LogoutPath = new PathString(Constants.SignOut);
     options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
    });

Those 2 rows that are comment LoginPath(/signin) and LogoutPath(/signout) after i comment them everything work as expected. Can some please give more information why i experience such behavior