/// <summary>
        /// Authenticates the client. Used when authenticating with the authorization_code grant type.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="redirectUri">The redirect URI.</param>
        /// <returns>The client principal.</returns>
        public override async Task<ISentinelPrincipal> AuthenticateClientAsync(string clientId, string redirectUri)
        {
            using (var connection = this.OpenConnection())
            {
                var transaction = connection.BeginTransaction();
                var matches =
                    await
                    connection.QueryAsync<Client>(
                        "SELECT * FROM Clients WHERE ClientId = @UserName AND RedirectUri = @RedirectUri AND Enabled = 1",
                        new { UserName = clientId, RedirectUri = redirectUri },
                        transaction);

                if (matches.Any())
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.OAuth,
                                new SentinelClaim(ClaimTypes.Name, clientId)));

                    return principal;
                }

                return SentinelPrincipal.Anonymous;
            }
        }
        private async Task OnPrincipalCreated(PrincipalCreatedEventArgs args)
        {
            var principal = new SentinelPrincipal(args.Principal);

            // Add name identifier claim to support MVC AntiForgeryToken
            principal.Identity.AddClaim(ClaimTypes.NameIdentifier, principal.Identity.Name);

            // Replace principal to make Sentinel use the new principal for the access token
            args.Principal = principal;
        }
        /// <summary>Authenticates the user using username and password.</summary>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <returns>The client principal.</returns>
        /// <exception cref="SqlException">Parallel transactions are not allowed when using Multiple Active Result Sets (MARS).</exception>
        public async override Task<ISentinelPrincipal> AuthenticateUserWithPasswordAsync(string username, string password)
        {
            using (var connection = this.OpenConnection())
            {
                var transaction = connection.BeginTransaction();
                var matches =
                    await
                    connection.QueryAsync<User>(
                        "SELECT * FROM Users WHERE UserName = @UserName",
                        new { UserName = username }, 
                        transaction);
                var user = matches.FirstOrDefault();

                if (user != null && this.CryptoProvider.ValidateHash(password, user.Password))
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.OAuth,
                                new SentinelClaim(ClaimTypes.Name, user.Username),
                                new SentinelClaim(ClaimTypes.GivenName, user.FirstName),
                                new SentinelClaim(ClaimTypes.Surname, user.LastName)));

                    // Update last login date
                    await
                        connection.ExecuteAsync(
                            "UPDATE Users SET LastLogin = @LastLogin WHERE Username = @Username",
                            new { LastLogin = DateTime.UtcNow, Username = user.Username },
                            transaction);

                    return principal;
                }

                return SentinelPrincipal.Anonymous;
            }
        }
        /// <summary>
        /// Converts the claim principal to a json string
        /// </summary>
        /// <param name="principal">The principal.</param>
        /// <returns>A json string representing the claims principal.</returns>
        public static string ToJson(this IPrincipal principal)
        {
            var p = new SentinelPrincipal(principal);

            return JsonConvert.SerializeObject(p);
        }
        /// <summary>
        /// Called when a request to the Token endpoint arrives with a "grant_type" of "refresh_token". This occurs if your application has issued a "refresh_token" 
        ///             along with the "access_token", and the client is attempting to use the "refresh_token" to acquire a new "access_token", and possibly a new "refresh_token".
        ///             To issue a refresh token the an Options.RefreshTokenProvider must be assigned to create the value which is returned. The claims and properties 
        ///             associated with the refresh token are present in the context.Ticket. The application must call context.Validated to instruct the 
        ///             Authorization Server middleware to issue an access token based on those claims and properties. The call to context.Validated may 
        ///             be given a different AuthenticationTicket or ClaimsIdentity in order to control which information flows from the refresh token to 
        ///             the access token. The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the refresh token to 
        ///             the access token unmodified.
        ///             See also http://tools.ietf.org/html/rfc6749#section-6
        /// </summary>
        /// <param name="context">The context of the event carries information in and results out.</param>
        /// <returns>
        /// Task to enable asynchronous execution
        /// </returns>
        public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
        {
            this.options.Logger.Debug("Authenticating refresh token flow");

            var user = new SentinelPrincipal(context.Ticket.Identity);

            // Add grant type claim
            user.Identity.RemoveClaim(x => x.Type == ClaimType.GrantType);
            user.Identity.AddClaim(ClaimType.GrantType, GrantType.RefreshToken);

            // Activate event if subscribed to
            if (this.options.Events.PrincipalCreated != null)
            {
                var args = new PrincipalCreatedEventArgs(user, context);

                await this.options.Events.PrincipalCreated(args);

                user = new SentinelPrincipal(args.Principal);
            }

            context.Validated(user.Identity.AsClaimsIdentity());
        }
        /// <summary>
        /// Called when a request to the Token endpoint arrives with a "grant_type" of "authorization_code". This occurs after the Authorize
        /// endpoint as redirected the user-agent back to the client with a "code" parameter, and the client is exchanging that for an "access_token".
        /// The claims and properties
        /// associated with the authorization code are present in the context.Ticket. The application must call context.Validated to instruct the Authorization
        /// Server middleware to issue an access token based on those claims and properties. The call to context.Validated may be given a different
        /// AuthenticationTicket or ClaimsIdentity in order to control which information flows from authorization code to access token.
        /// The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the authorization code to
        /// the access token unmodified.
        /// See also http://tools.ietf.org/html/rfc6749#section-4.1.3
        /// </summary>
        /// <param name="context">The context of the event carries information in and results out.</param>
        /// <returns>Task to enable asynchronous execution</returns>
        public override async Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
        {
            this.options.Logger.Debug("Authenticating authorization code flow");

            var user = new SentinelPrincipal(context.Ticket.Identity);

            // Add grant type claim
            user.Identity.RemoveClaim(x => x.Type == ClaimType.GrantType);
            user.Identity.AddClaim(ClaimType.GrantType, GrantType.AuthorizationCode);

            context.Validated(user.Identity.AsClaimsIdentity());
        }
        /// <summary>
        /// Called when a request to the Token endpoint arrives with a "grant_type" of "password". This occurs when the user has provided name and password
        /// credentials directly into the client application's user interface, and the client application is using those to acquire an "access_token" and
        /// optional "refresh_token". If the web application supports the
        /// resource owner credentials grant type it must validate the context.Username and context.Password as appropriate. To issue an
        /// access token the context.Validated must be called with a new ticket containing the claims about the resource owner which should be associated
        /// with the access token. The application should take appropriate measures to ensure that the endpoint isn’t abused by malicious callers.
        /// The default behavior is to reject this grant type.
        /// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
        /// </summary>
        /// <param name="context">The context of the event carries information in and results out.</param>
        /// <returns>Task to enable asynchronous execution</returns>
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            this.options.Logger.DebugFormat("Authenticating resource owner flow for user '{0}'", Regex.Escape(context.UserName));

            var user = await this.options.UserManager.AuthenticateUserWithPasswordAsync(context.UserName, context.Password);

            if (!user.Identity.IsAuthenticated)
            {
                context.Rejected();

                this.options.Logger.WarnFormat("User '{0}' was not authenticated", Regex.Escape(context.UserName));

                return;
            }

            // Add oauth claims
            user.Identity.AddClaim(ClaimType.Client, context.ClientId);
            user.Identity.RemoveClaim(x => x.Type == ClaimType.GrantType);
            user.Identity.AddClaim(ClaimType.GrantType, GrantType.Password);

            // Activate event if subscribed to
            if (this.options.Events.PrincipalCreated != null)
            {
                var args = new PrincipalCreatedEventArgs(user, context);

                await this.options.Events.PrincipalCreated(args);

                user = new SentinelPrincipal(args.Principal);
            }

            // Convert to proper authentication type
            var principal = this.options.PrincipalProvider.Create(context.Options.AuthenticationType, user.Identity.Claims.ToArray());

            // Validate ticket
            var ticket = new AuthenticationTicket(principal.Identity.AsClaimsIdentity(), new AuthenticationProperties());

            context.Validated(ticket);

            this.options.Logger.DebugFormat("User '{0}' was successfully authenticated", Regex.Escape(context.UserName));
        }
        /// <summary>Authenticates the client credentials using client id and secret.</summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="clientSecret">The client secret.</param>
        /// <returns>The client principal.</returns>
        public override async Task<ISentinelPrincipal> AuthenticateClientCredentialsAsync(string clientId, string clientSecret)
        {
            using (var connection = this.OpenConnection())
            {
                var transaction = connection.BeginTransaction();
                var matches =
                    await
                    connection.QueryAsync<Client>(
                        "SELECT * FROM Clients WHERE ClientId = @UserName AND Enabled = 1", 
                        new { UserName = clientId }, 
                        transaction);

                foreach (var client in matches)
                {
                    if (this.CryptoProvider.ValidateHash(clientSecret, client.ClientSecret))
                    {
                        var principal =
                            new SentinelPrincipal(
                                new SentinelIdentity(
                                    AuthenticationType.OAuth, 
                                    new SentinelClaim(ClaimTypes.Name, client.ClientId)));

                        return principal;
                    }
                }

                return SentinelPrincipal.Anonymous;
            }
        }
        public void Decrypt_WhenGivenEncryptedString_ReturnsDecryptedString(string key)
        {
            var c1 = new SentinelPrincipal(new SentinelIdentity(AuthenticationType.OAuth, new SentinelClaim(ClaimTypes.Name, "azzlack")));
            var s = JsonConvert.SerializeObject(c1);

            var e = this.provider.Encrypt(s, key);

            Console.WriteLine("Original: {0}", s);
            Console.WriteLine();
            Console.WriteLine("Encrypted: {0}", e);
            Console.WriteLine();

            var d = this.provider.Decrypt(e, key);

            Console.WriteLine("Decrypted: {0}", d);

            var c2 = JsonConvert.DeserializeObject<SentinelPrincipal>(d);

            Assert.AreEqual(c1.Identity.Name, c2.Identity.Name);
        }