public BearerAuthenticationProvider(WeakCache cache)
        {
            ThrowNullArguments(() => cache);

            _cache = cache;

            //make sure a logon hasnt been invalidated!
            OnValidateIdentity = context => Task.Run(() =>
            {
                var token = context.Request.Headers.Get("Authorization").TrimStart("Bearer").Trim();

                //in future, a realtime event will notify the bearer-provider of changes to a logon, so we dont need to keep quering the database
                var logon = _cache.GetOrRefresh <UserLogon>(token);

                //Note that if "logon"' is null, it means that it was not found either in the cache or in the db - but the fact that this method was called
                //means that the token was verified by the authorization server: this is an anomaly, as the source of the token is in question. What we do
                //is reject this request.
                if (logon?.Invalidated ?? true)
                {
                    context.Rejected();
                }

                //implement traditional session timeout after "WebConstants.Misc_SessionTimeoutMinutes" minutes
                else if ((DateTime.Now - logon.ModifiedOn) >= TimeSpan.FromMinutes(WebConstants.Misc_SessionTimeoutMinutes))
                {
                    logon.ModifiedOn = DateTime.Now;
                    context.Rejected();
                }
                else
                {
                    context.OwinContext.Environment[WebConstants.Misc_UserLogonOwinContextKey] = logon;
                    context.Validated();
                }
            });
        }
        public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        => Task.Run(() =>
        {
            var _credentialAuthority = context.OwinContext.GetPerRequestValue <ICredentialAuthentication>(nameof(ICredentialAuthentication));
            var _dataContext         = context.OwinContext.GetPerRequestValue <IDataContext>(nameof(IDataContext));

            #region delete old logons if they exist
            Operation.Try(() =>
            {
                var oldToken = context.Request.Headers.GetValues(WebConstants.OAuthCustomHeaders_OldToken)?.FirstOrDefault() ?? null;
                if (oldToken != null)
                {
                    var logon = _cache.GetOrRefresh <UserLogon>(oldToken);
                    if (logon != null)
                    {
                        logon.Invalidated = true;
                        _dataContext.Store <UserLogon>().Modify(logon, true);
                        _cache.Invalidate(oldToken);
                    }
                }
            })
            #endregion

            #region Verify the user exists
            .Then(opr =>
            {
                return(_dataContext.Store <User>().Query
                       .Where(_u => _u.EntityId == context.UserName)
                       .FirstOrDefault()
                       .ThrowIfNull("invalid user credential")
                       .ThrowIf(_u => _u.Status != (int)AccountStatus.Active, "inactive user account"));
            })
            #endregion

            #region verify credentials with the credential authority
            .Then(opr =>
            {
                _credentialAuthority.VerifyCredential(new Credential
                {
                    OwnerId  = context.UserName,
                    Metadata = CredentialMetadata.Password,
                    Value    = Encoding.UTF8.GetBytes(context.Password)
                })
                .Resolve();
                return(opr.Result);
            })
            #endregion

            #region aggregate the claims that makeup the token
            .Then(opr =>
            {
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);

                //identity.AddClaim(new Claim("user-name", context.UserName));
                identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
                identity.AddClaim(new Claim(ClaimTypes.Sid, opr.Result.UId.ToString()));
                identity.AddClaim(new Claim("user-status", opr.Result.Status.ToString()));

                //roles
                _dataContext.Store <UserRole>().Query
                .Where(_ur => _ur.UserId == opr.Result.EntityId)
                .ForAll((_cnt, _next) => identity.AddClaim(new Claim(ClaimTypes.Role, _next.RoleName)));

                context.Validated(new Microsoft.Owin.Security.AuthenticationTicket(identity, null));
            })
            #endregion

            #region if any of the above failed...
            .Error(opr =>
            {
                context.SetError("invalid_grant", opr.Message);
                context.Rejected();
            });
            #endregion
        });