1

We have a ServiceStack service being accessed by an Angular website. Randomly and we cannot identify why or when it happens, the bearer token is being invalidated. We can see the bearer token is not accepted with error message. "Token has been invalidated". I know for sure we have not restarted the servicestack service and we are still passing the original bearer token in the request. We do not have any logic implemented to invalidate a bearer token. I know this is somewhat vague. If anyone can point me to how to trouble shoot this in ServiceStack. that is what I need.

Using servicestack 5.4.1

namespace cbw.mvc.web.service
{

    public class AppHost : AppHostBase
    {
        public AppHost() : base("ServiceStack + .NET Core", typeof(StartupService).Assembly) { }

        public override void Configure(Funq.Container container)
        {
            Plugins.Add(new SwaggerFeature());

            Plugins.Add(new RazorFormat());

            //Works but recommend handling 404 at end of .NET Core pipeline
            //this.CustomErrorHttpHandlers[HttpStatusCode.NotFound] = new RazorHandler("/notfound");
            this.CustomErrorHttpHandlers[HttpStatusCode.Unauthorized] = new RazorHandler("/login");

            //To include null values in the json globally
            JsConfig.IncludeNullValues = true;
            //This is mandate. We need "IncludeNullValuesInDictionaries = true" to include null values
            JsConfig.IncludeNullValuesInDictionaries = true;


            var corsWhitelist = AppSettings.GetList("cors.whitelist.urls");

            //To automatically wired up for you on all HTTP Verbs (GET, POST, etc) 
            //And built-in endpoints, i.e. DeviceConfigValues, XML, JSV, HTML, CSV, SOAP
            Plugins.Add(new CorsFeature(
                allowOriginWhitelist: corsWhitelist,
                allowCredentials: true,
                allowedHeaders: "Content-Type, Allow, Authorization,UserId,CompanyId"));

            //To add registration feature
            Plugins.Add(new RegistrationFeature());

            //To add validation feature
            Plugins.Add(new ValidationFeature());
            container.RegisterValidators(
                typeof(InsertCompanyValidator).Assembly,
                typeof(UpdateCompanyValidator).Assembly,
                typeof(DeleteCompanyValidator).Assembly,

                typeof(InsertDeviceTypeValidator).Assembly,
                typeof(UpdateDeviceTypeValidator).Assembly,
                typeof(DeleteDeviceTypeValidator).Assembly,

                typeof(InsertLocationValidator).Assembly,
                typeof(UpdateLocationValidator).Assembly,
                typeof(DeleteLocationValidator).Assembly,

                //typeof(InsertRolePermissionValidator).Assembly,
                //typeof(UpdateRolePermissionValidator).Assembly,

                //Page Validator
                typeof(AddSecPageValidator).Assembly,
                typeof(UpdateSecPageValidator).Assembly,
                typeof(DeleteSecPageValidator).Assembly,

                //Page Permission Validator
                typeof(AddPagePermissionValidator).Assembly,
                typeof(UpdatePagePermissionValidator).Assembly,

                //SecGroup Validator
                typeof(AddSecGroupValidator).Assembly,
                typeof(UpdateSecGroupValidator).Assembly,
                typeof(DeleteSecGroupValidator).Assembly,

                //GroupRole Validator
                typeof(AddGroupRoleValidator).Assembly,
                typeof(UpdateGroupRoleValidator).Assembly,
                typeof(DeleteGroupRoleValidator).Assembly,

                //UserGroup Validator
                typeof(AddUserGroupValidator).Assembly,
                typeof(UpdateUserGroupValidator).Assembly,
                typeof(DeleteUserGroupValidator).Assembly,

                //GroupCompany Validator
                typeof(AddGroupCompanyValidator).Assembly,
                typeof(UpdateGroupCompanyValidator).Assembly,
                typeof(DeleteGroupCompanyValidator).Assembly,

                //Document Validator
                typeof(AddDocumentValidator).Assembly,
                typeof(UpdateDocumentValidator).Assembly,
                typeof(DeleteDocumentValidator).Assembly,

                //DocumentType Validator
                typeof(AddDocumentTypeValidator).Assembly,
                typeof(UpdateDocumentValidator).Assembly,
                typeof(DeleteDocumentValidator).Assembly,

                 // IoSetup Validator
                 typeof(DeviceIOSetupLocalDigitalInputAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteDigitalInputAddValidator).Assembly,
                 typeof(DeviceIOSetupExpansionDigitalInputAddValidator).Assembly,
                 typeof(DeviceIOSetupLocalRelayAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteRelayAddValidator).Assembly,
                 typeof(DeviceIOSetupExpansionRelayAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteAnalogOutputAddValidator).Assembly,
                 typeof(DeviceIOSetupLocalAnalogInputAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteAnalogInputAddValidator).Assembly,
                 typeof(DeviceIOSetupExpansionAnalogInputAddValidator).Assembly,
                 typeof(DeviceIOSetupLocalDigitalIOAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteDigitalIOAddValidator).Assembly,
                 typeof(DeviceIOSetupLocalOneWireAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteOneWireAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteThermocoupleAddValidator).Assembly,
                 typeof(DeviceIOSetupExpansionThermocoupleAddValidator).Assembly,
                 typeof(DeviceIOSetupLocalRegisterAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteRegisterAddValidator).Assembly,
                 typeof(DeviceIOSetupLocalVinAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteVinAddValidator).Assembly,
                 typeof(DeviceIOSetupLocalTimerAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteBatteryAddValidator).Assembly,
                 typeof(DeviceIOSetupLocalFrequencyInputAddValidator).Assembly,
                 typeof(DeviceIOSetupRemoteFrequencyInputAddValidator).Assembly
            );

            Plugins.Add(new AuthFeature(() => new CustomUserSession(),
                new IAuthProvider[]
                {
                    //new BasicAuthProvider(),                    //Sign-in with HTTP Basic Auth
                    new JwtAuthProvider(AppSettings) {
                        //HashAlgorithm = "HM256",
                        //PrivateKey = privateKey.ExportParameters(true),
                        AuthKeyBase64 = AppSettings.GetString("jwt.auth.key"),
                        RequireSecureConnection = false,
                        InvalidateTokensIssuedBefore = DateTime.Now,
                        ExpireTokensIn = TimeSpan.FromHours(24)
                        //Turn on for Prod: EncryptPayload = true
                        }, //JWT TOKENS
                    new CredentialsAuthProvider(AppSettings)
                })
            {
                HtmlRedirect = "/",
                //IncludeRegistrationService = true,
            });

            //Permit modern browsers (e.g. Firefox) to allow sending of any HTTP Method
            //SetConfig(new HostConfig
            //{
            //    GlobalResponseHeaders = {
            //        { "Access-Control-Allow-Origin", "*" },
            //        { "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" },
            //        { "Access-Control-Allow-Headers", "Content-Type" },
            //    },
            //});


            //AutoQuery
            Plugins.Add(new AutoQueryFeature { MaxLimit = 100000 });
            //Cache
            container.Register<ICacheClient>(new MemoryCacheClient());

            container.Register<IAuthRepository>(c =>
                new MyOrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())
                {
                    UseDistinctRoleTables = AppSettings.Get("UseDistinctRoleTables", true),
                });
            OrmLiteConfig.BeforeExecFilter = dbCmd => Debug.WriteLine(dbCmd.GetDebugString());

            bool ShouldWipeAndReloadDb = false;
            var environmentVariable = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

            if (environmentVariable == "LocalMemory" || environmentVariable == "LocalSQLServer")
            {
                ShouldWipeAndReloadDb = true;
                //Init auth tables
                container.Resolve<IAuthRepository>().InitSchema();
            }
            var authRepo = (MyOrmLiteAuthRepository)container.Resolve<IAuthRepository>();

            //Wipe and reload if using in memory SQL
            if (ShouldWipeAndReloadDb)
            {
                DatabaseInitService dis = new DatabaseInitService();
                dis.ResetDatabase();
                SessionService.ResetUsers(authRepo);
                dis.InitializeTablesAndData();
            }
        }
    }
}

public class CustomUserSession : AuthUserSession
{
    [DataMember]
    public string CustomName { get; set; }

    [DataMember]
    public string CustomInfo { get; set; }

}
Steve Coleman
  • 1,987
  • 2
  • 17
  • 28

1 Answers1

1

The error message "Token has been invalidated" only occurs when the issue date of the JWT Token was issued before InvalidateTokensIssuedBefore where you've configured to be:

InvalidateTokensIssuedBefore = DateTime.Now,

It's unlikely you want to invalidate Tokens whenever the App Domain is restarted, it's akin to never having a persistent Auth Key (i.e. just using a transient AesUtils.CreateKey()) since any JWT's created in between App Domain restarts/recylcles are automatically invalidated.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • Setting it to InvalidateTokensIssuedBefore = DateTime.Now.AddHours(24) caused it to not be able to login at all. commenting out the line resolved the issue. – Steve Coleman Nov 13 '19 at 21:17
  • Additionally, we are running this in an Azure App Service. when we overloaded the app service it caused all the bearer tokens to expire with the datetime.now setting – Steve Coleman Nov 13 '19 at 21:29
  • @SteveColeman Expected as it would invalidate every JWT created within the next 24 hours. Note: the [Issued At claim (iat) is stored in Unix Time](https://stackoverflow.com/a/39926886/85785) so you should also use UTC for date comparisons, e.g. `DateTime.UtcNow.AddDays(-1)` but it's not how JWT's should be used, your systems should be designed so JWT's are valid up until the expiry date (i.e. implicit contract of JWT's) which you've set to `TimeSpan.FromHours(24)`. `InvalidateTokensIssuedBefore` is an escape hatch when needing to immediately invalidate all JWTs which should be a constant date. – mythz Nov 13 '19 at 21:33