At Advise we use Harvest for time tracking. On top of Harvest we have build a separate system that we use to keep track of statistics, vaccation periods and salary specifications. All information is fetched from Harvest. Login to the system is done using via Harvest.

We recently migrated this system to ASP.NET Core 2.0 and needed to update the type of authentication used the authentication process has been completely rewritten in ASP.NET Core 2.0.

To make it easier to use we put together a small library called Harvest Connect that helps with this integration. You can grab it from Nuget and the source code is available on Github. What follows is a how to guide on how to use Harvest Connect.

First of all you need to get a clientId and a clientSecret from Harvest. To do this visit the settings section of your Harvest site.

Once setup, grab the HarvestConnect package from Nuget:

dotnet add HarvestConnect

and modify your ConfigureServices method in your Startup.cs class. In the example we use cookies to keep login information for the user but you could just as well use JWT or some other type of storage.

services.AddAuthentication(options =>
    options.DefaultAuthenticateScheme = HarvestOptions.Scheme;
    options.DefaultChallengeScheme = HarvestOptions.Scheme;
    options.DefaultSignInScheme = "auth";
    options.DefaultSignOutScheme = "auth";
.AddCookie("auth", options =>
    options.LogoutPath = "/logout";
.AddHarvest(options =>
    options.ClientId = "<provided by Harvest>";
    options.ClientSecret = "<provided by harvest>";
    options.BaseUrl = "<base url of your site, e.g. http://localhost:13370>";
    options.Events = new HarvestEvents
        // Called on succesful login
        OnSuccessfulLogin = context =>
            // Add the claims your app requires...
            context.Identity.AddClaim(new Claim(context.Identity.NameClaimType,

            if (context.WhoAmI.user.admin)
                context.Identity.AddClaim(new Claim(context.Identity.RoleClaimType, "Administrator"));

            context.Identity.AddClaim(new Claim("Email",;

            // ... or do other stuff with the data received, e.g.:
            // await _db.UpdateAvatar(, context.WhoAmI.user.avatar_url);

            return Task.CompletedTask;
        // Called when login fails or the user denies the login attempt
        OnFailedLogin = async context =>
            await context.HttpContext.SignOutAsync();
            context.HttpContext.Response.Redirect("/home/logout?message=" + WebUtility.UrlEncode(context.FailureMessage));