3

I have implemented Identity Core on my .Net5 application to handle login, logout, register and refresh with 4 endpoints.

When I call the logout endpoint and send the refresh token it deletes the refresh token from the database so the user can't refresh the access token. The problem is that I can still use the access token to call the endpoints of my application and obtain the authorization until its regular expiration.

I'd like to know if there is a way to invalidate the access token after the logout without waiting for the expiration.

This is my ConfigureService on the Startup:

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "AuthenticationAndAuthorizationAPI", Version = "v1" });
            });
            services.AddIdentityCore<User>(o =>
            {
                o.User.RequireUniqueEmail = true;
                o.Password.RequireDigit = false;
                o.Password.RequireNonAlphanumeric = false;
                o.Password.RequireUppercase = false;
                o.Password.RequiredLength = 0;
            }).AddEntityFrameworkStores<AuthenticationDbContext>();

            AuthenticationConfiguration authenticationConfiguration = new();
            _configuration.Bind("Authentication", authenticationConfiguration);

            services.AddSingleton(authenticationConfiguration);

            services.AddEntityFrameworkNpgsql().AddDbContext<AuthenticationDbContext>(options =>
             options.UseNpgsql(_configuration.GetConnectionString("DBConnection")));

            services.AddSingleton<AccessTokenGenerator>();
            services.AddSingleton<RefreshTokenGenerator>();
            services.AddSingleton<RefreshTokenValidator>();
            services.AddScoped<Authenticator>();
            services.AddSingleton<TokenGenerator>();
            services.AddScoped<IRefreshTokenRepository, DatabaseRefreshTokenRepository>();

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
            {
                o.TokenValidationParameters = new TokenValidationParameters()
                {
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authenticationConfiguration.AccessTokenSecret)),
                    ValidIssuer = authenticationConfiguration.Issuer,
                    ValidAudience = authenticationConfiguration.Audience,
                    ValidateIssuerSigningKey = true,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ClockSkew = TimeSpan.Zero
                };
            });
        }
Ermiya Eskandary
  • 15,323
  • 3
  • 31
  • 44
  • "When I call the logout endpoint and send the refresh token it deletes the refresh token from the database so the user can't refresh the access token. The problem is that I can still use the access token to call the endpoints of my application and obtain the authorization until its regular expiration." - you delete the refresh token from the db or the access token? Do you want to invalidate the refresh token or the access token? You have a typo – Ermiya Eskandary Oct 08 '21 at 09:34
  • 1
    I delete the refresh token from the db so I can't ask for a new access token but the last one still works until the expiration and I like to invalidate that last access token when I logout. – Federico Alberti Oct 08 '21 at 09:57

1 Answers1

3

You cannot "invalidate" JWT tokens - you have a few options here.

A few are workarounds like keeping token expiry times short so that the attack window is shorter or removing the token client-side when the application logs out still keeps the problem of the "attacker" stealing the key beforehand.

To force a real server-side invalidation, you can sign the JWT token with a lastLogoutTs value, which is updated on every logout. Once the user logs out, their lastLogoutTs value is changed in the database meaning that the JWT token is no longer valid for usage & cannot be verified for access. This obviously means you are hitting the database on every logout, but it genuinely isn't as bad as it sounds as you are probably hitting the DB to load the user up to log them out anyway.

Alternatively, instead of an extra field, you can keep a blocklist table in an in-memory database which allows you to put an expiration time on a key like Redis, Memcached etc. This table would store the access tokens for users who have logged out, who have not yet met the expiry date on the token. In Redis, for example, you could use EXPIRE to automatically remove the access tokens while also being incredibly fast (as it is an in-memory database).

For a solution that must not use a database (can't think of any), JWTs would not be suitable & something like OAuth 2.0 would be better (which eventually, offloads the logout logic to a provider like Google, Apple etc.).

Ermiya Eskandary
  • 15,323
  • 3
  • 31
  • 44