/// <summary>
        /// Authenticates the user using username only. This method is used to get new user claims after
        /// a refresh token has been used. You can therefore assume that the user is already logged in.
        /// </summary>
        /// <param name="username">The username.</param>
        /// <returns>The user principal.</returns>
        public override async Task <ISentinelPrincipal> AuthenticateUserAsync(string username)
        {
            var user = await this.UserRepository.GetUser(username);

            if (user != null)
            {
                var principal =
                    new SentinelPrincipal(
                        new SentinelIdentity(
                            AuthenticationType.OAuth,
                            new SentinelClaim(ClaimTypes.Name, user.UserId),
                            new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.UserId),
                            new SentinelClaim(ClaimTypes.GivenName, user.FirstName),
                            new SentinelClaim(ClaimTypes.Surname, user.LastName)));

                if (principal.Identity.IsAuthenticated)
                {
                    // TODO: Update last login date
                    //await
                    //    connection.ExecuteAsync(
                    //        "UPDATE Users SET LastLogin = @LastLogin WHERE Username = @Username",
                    //        new { LastLogin = DateTimeOffset.UtcNow, Username = user.Username },
                    //        transaction);

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
        /// <summary>
        /// Authenticates the client. Used when authenticating with the client_credentials grant type.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="scope">The redirect URI.</param>
        /// <returns>The client principal.</returns>
        public override async Task <ISentinelPrincipal> AuthenticateClientAsync(string clientId, IEnumerable <string> scope)
        {
            var client = await this.ClientRepository.GetClient(clientId);

            if (client != null && client.Enabled)
            {
                var principal =
                    new SentinelPrincipal(
                        new SentinelIdentity(
                            AuthenticationType.OAuth,
                            new SentinelClaim(ClaimTypes.Name, clientId),
                            new SentinelClaim(ClaimType.RedirectUri, client.RedirectUri),
                            new SentinelClaim(ClaimType.Scope, string.Join(" ", scope)),
                            new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.ClientId)));

                if (principal.Identity.IsAuthenticated)
                {
                    // TODO: Update lastused date

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
Exemple #3
0
        /// <summary>Authenticate the user using an API key.</summary>
        /// <param name="digest">The digest.</param>
        /// <returns>The user principal.</returns>
        public override async Task <ISentinelPrincipal> AuthenticateUserWithSignatureAsync(SignatureAuthenticationDigest digest)
        {
            var userKeys = await this.UserApiKeyRepository.GetForUser(digest.UserId);

            var matchingKey = this.ValidateApiKey(userKeys, digest.GetData(), digest.Signature);

            if (matchingKey != null)
            {
                var user = await this.UserRepository.GetUser(matchingKey.UserId);

                if (user != null)
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.Signature,
                                new SentinelClaim(JwtClaimType.Name, user.UserId),
                                new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.ApiKey),
                                new SentinelClaim(ClaimType.AuthenticationSource, "local"),
                                new SentinelClaim(JwtClaimType.GivenName, user.FirstName),
                                new SentinelClaim(JwtClaimType.FamilyName, user.LastName)));

                    user.LastLogin = DateTimeOffset.UtcNow;
                    await this.UserRepository.Update(user.GetIdentifier(), user);

                    matchingKey.LastUsed = DateTimeOffset.UtcNow;
                    await this.UserApiKeyRepository.Update(matchingKey.GetIdentifier(), matchingKey);

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
Exemple #4
0
        /// <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>
        public override async Task <ISentinelPrincipal> AuthenticateUserWithPasswordAsync(string username, string password)
        {
            var user = await this.UserRepository.GetUser(username);

            if (user != null && this.PasswordCryptoProvider.ValidateHash(password, user.Password))
            {
                var principal =
                    new SentinelPrincipal(
                        new SentinelIdentity(
                            AuthenticationType.OAuth,
                            new SentinelClaim(JwtClaimType.Name, user.UserId),
                            new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.UserCredentials),
                            new SentinelClaim(JwtClaimType.GivenName, user.FirstName),
                            new SentinelClaim(JwtClaimType.FamilyName, user.LastName)));

                if (principal.Identity.IsAuthenticated)
                {
                    user.LastLogin = DateTimeOffset.UtcNow;
                    await this.UserRepository.Update(user.GetIdentifier(), user);

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
        /// <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)
        {
            var client = await this.ClientRepository.GetClient(clientId);

            if (client != null && client.Enabled && client.RedirectUri == redirectUri)
            {
                var principal =
                    new SentinelPrincipal(
                        new SentinelIdentity(
                            AuthenticationType.OAuth,
                            new SentinelClaim(JwtClaimType.Name, clientId),
                            new SentinelClaim(ClaimTypes.NameIdentifier, client.ClientId),
                            new SentinelClaim(ClaimType.RedirectUri, client.RedirectUri),
                            new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.ClientId)));

                if (principal.Identity.IsAuthenticated)
                {
                    client.LastUsed = DateTimeOffset.UtcNow;
                    await this.ClientRepository.Update(client.GetIdentifier(), client);

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
        /// <summary>
        /// Authenticates the user using username only. This method is used to get new user claims after
        /// a refresh token has been used. You can therefore assume that the user is already logged in.
        /// </summary>
        /// <param name="username">The username.</param>
        /// <returns>The user principal.</returns>
        public override async Task <ISentinelPrincipal> AuthenticateUserAsync(string username)
        {
            var user = await this.UserRepository.GetUser(username);

            if (user != null && user.Enabled)
            {
                var principal =
                    new SentinelPrincipal(
                        new SentinelIdentity(
                            AuthenticationType.OAuth,
                            new SentinelClaim(JwtClaimType.Name, user.UserId),
                            new SentinelClaim(ClaimTypes.NameIdentifier, user.UserId),
                            new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.UserId),
                            new SentinelClaim(JwtClaimType.GivenName, user.FirstName),
                            new SentinelClaim(JwtClaimType.FamilyName, user.LastName)));

                if (principal.Identity.IsAuthenticated)
                {
                    user.LastLogin = DateTimeOffset.UtcNow;
                    await this.UserRepository.Update(user.GetIdentifier(), user);

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
        /// <summary>Authenticates the client 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)
        {
            var client = await this.ClientRepository.GetClient(clientId);

            if (client != null && client.Enabled)
            {
                if (this.CryptoProvider.ValidateHash(clientSecret, client.ClientSecret))
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.OAuth,
                                new SentinelClaim(ClaimTypes.Name, client.ClientId),
                                new SentinelClaim(ClaimType.RedirectUri, client.RedirectUri),
                                new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.ClientCredentials)));

                    if (principal.Identity.IsAuthenticated)
                    {
                        // TODO: Update lastused date

                        return(principal);
                    }
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
Exemple #8
0
        /// <summary>Authenticates the client 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)
        {
            var client = await this.ClientRepository.GetClient(clientId);

            if (client != null && client.Enabled)
            {
                if (this.PasswordCryptoProvider.ValidateHash(clientSecret, client.ClientSecret))
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.OAuth,
                                new SentinelClaim(JwtClaimType.Name, client.ClientId),
                                new SentinelClaim(ClaimType.Client, client.ClientId),
                                new SentinelClaim(ClaimType.RedirectUri, client.RedirectUri),
                                new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.ClientCredentials)));

                    if (principal.Identity.IsAuthenticated)
                    {
                        client.LastUsed = DateTimeOffset.UtcNow;
                        await this.ClientRepository.Update(client.GetIdentifier(), client);

                        return(principal);
                    }
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
Exemple #9
0
        /// <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);

            // Set scopes from refresh token
            if (user.Identity.HasClaim(x => x.Type == ClaimType.Scope))
            {
                context.OwinContext.GetOAuthContext().Scope = user.Identity.Claims.Where(x => x.Type == ClaimType.Scope).Select(x => x.Value);
            }

            // 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());
        }
        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;
        }
Exemple #11
0
        public void Encrypt_WhenGivenValidPrincipal_ReturnsEncryptedPrincipal(string key)
        {
            var c1 = new SentinelPrincipal(new SentinelIdentity("Test", new SentinelClaim(ClaimTypes.Name, "azzlack")));

            var r = this.principalProvider.Encrypt(c1, key);

            Console.WriteLine("Encrypted: {0}", r);

            Assert.IsNotNullOrEmpty(r);
        }
Exemple #12
0
        public void Decrypt_WhenGivenValidPrincipal_ReturnsDecryptedPrincipal(string key)
        {
            var c1 = new SentinelPrincipal(new SentinelIdentity("Test", new SentinelClaim(ClaimTypes.Name, "azzlack")));

            var r = this.principalProvider.Encrypt(c1, key);

            Console.WriteLine("Encrypted: {0}", r);

            var c2 = this.principalProvider.Decrypt(r, key);

            Assert.AreEqual(c1.Identity.Name, c2.Identity.Name);
        }
Exemple #13
0
        /// <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());
        }
Exemple #14
0
        /// <summary>Authenticate the user using an API key.</summary>
        /// <param name="digest">The digest.</param>
        /// <returns>The user principal.</returns>
        public override async Task <ISentinelPrincipal> AuthenticateUserWithApiKeyAsync(BasicAuthenticationDigest digest)
        {
            string data;
            string signature;

            try
            {
                data      = this.PasswordCryptoProvider.CreateHash(256);
                signature = this.AsymmetricCryptoProvider.Sign(data, digest.Password);
            }
            catch (ArgumentException)
            {
                return(SentinelPrincipal.Anonymous);
            }
            catch (FormatException)
            {
                return(SentinelPrincipal.Anonymous);
            }

            var userKeys = await this.UserApiKeyRepository.GetForUser(digest.UserId);

            var matchingKey = this.ValidateApiKey(userKeys, data, signature);

            if (matchingKey != null)
            {
                var user = await this.UserRepository.GetUser(matchingKey.UserId);

                if (user != null)
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.Signature,
                                new SentinelClaim(JwtClaimType.Name, user.UserId),
                                new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.ApiKey),
                                new SentinelClaim(ClaimType.AuthenticationSource, "local"),
                                new SentinelClaim(JwtClaimType.GivenName, user.FirstName),
                                new SentinelClaim(JwtClaimType.FamilyName, user.LastName)));

                    user.LastLogin = DateTimeOffset.UtcNow;
                    await this.UserRepository.Update(user.GetIdentifier(), user);

                    matchingKey.LastUsed = DateTimeOffset.UtcNow;
                    await this.UserApiKeyRepository.Update(matchingKey.GetIdentifier(), matchingKey);

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
        /// <summary>Authenticate client using API Key.</summary>
        /// <exception cref="ArgumentException">
        /// Thrown when one or more arguments have unsupported or illegal values.
        /// </exception>
        /// <param name="digest">The digest.</param>
        /// <returns>The client principal.</returns>
        public override async Task <ISentinelPrincipal> AuthenticateClientWithSignatureAsync(SignatureAuthenticationDigest digest)
        {
            // 1. Validate client id and redirect uri
            var client = await this.ClientRepository.GetClient(digest.ClientId);

            if (client == null || !client.Enabled)
            {
                throw new ArgumentException(nameof(digest), "The client_id is invalid");
            }

            if (client.RedirectUri != digest.RedirectUri)
            {
                throw new ArgumentException(nameof(digest), "The redirect_uri is invalid");
            }

            // 2. Validate username and signature using client secret
            if (digest.UserId != client.ClientId)
            {
                throw new ArgumentException(nameof(digest), "The user_id is invalid, must be equal to client_id");
            }

            if (!string.IsNullOrEmpty(client.PublicKey))
            {
                var isValid = this.AsymmetricCryptoProvider.ValidateSignature(digest.GetData(), digest.Signature, client.PublicKey);
                if (isValid)
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.Signature,
                                new SentinelClaim(JwtClaimType.Name, client.ClientId),
                                new SentinelClaim(ClaimTypes.NameIdentifier, client.ClientId),
                                new SentinelClaim(ClaimType.Client, client.ClientId),
                                new SentinelClaim(ClaimType.RedirectUri, client.RedirectUri),
                                new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.Signature)));

                    if (principal.Identity.IsAuthenticated)
                    {
                        client.LastUsed = DateTimeOffset.UtcNow;
                        await this.ClientRepository.Update(client.GetIdentifier(), client);

                        return(principal);
                    }

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
Exemple #16
0
        /// <summary>Refreshes the token.</summary>
        /// <param name="context">The current OWIN context.</param>
        /// <param name="options">The authentication options.</param>
        /// <returns>A Task.</returns>
        public virtual async Task OnRefresh(IOwinContext context, SentinelAuthenticationOptions options)
        {
            var user = new SentinelPrincipal(context.Authentication.User);

            // Always return json from this endpoint
            context.Response.ContentType = "application/json";

            // Dont refresh if user is authenticated and more than 1 minute remains of its validness
            if (user.Identity.IsAuthenticated && user.ValidTo.Subtract(DateTimeOffset.UtcNow) > TimeSpan.FromMinutes(1))
            {
                return;
            }

            var refreshCookie = context.Request.Cookies.FirstOrDefault(x => x.Key == $"{options.CookieConfiguration.Name}_RT");

            if (refreshCookie.Value != null)
            {
                var refreshTokenResponse = await options.TicketHandler.RefreshTokenAsync(context, options, refreshCookie.Value, options.RedirectUri);

                if (refreshTokenResponse != null)
                {
                    // Sign in as sentinel identity
                    var props = new AuthenticationProperties()
                    {
                        RedirectUri = context.Request.Uri.ToString()
                    };
                    var ticket = await options.TicketHandler.SignInAsync(context, options, refreshTokenResponse, props);

                    await options.Events.OnTokenRefreshed(context, ticket, options);

                    await context.Response.WriteAsync(JsonConvert.SerializeObject(refreshTokenResponse));

                    return;
                }

                options.Logger.WriteError("Refresh token found, but was unable to use it to retrieve a new access token");

                // Delete refresh token if it didnt work
                context.Response.Cookies.Delete($"{options.CookieConfiguration.Name}_RT", new CookieOptions()
                {
                    Domain = context.Request.Uri.Host, Secure = context.Request.IsSecure
                });
            }

            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            await context.Response.WriteAsync(JsonConvert.SerializeObject(new ErrorResponse("invalid_refresh_token")));
        }
        public override async Task <ISentinelPrincipal> AuthenticateClientCredentialsAsync(BasicAuthenticationDigest digest)
        {
            BasicAuthenticationCipher cipher;

            try
            {
                cipher = digest.GetCipher();
            }
            catch (ArgumentException)
            {
                return(SentinelPrincipal.Anonymous);
            }

            var client = await this.ClientRepository.GetClient(digest.UserId);

            if (client != null && client.Enabled && client.RedirectUri == cipher.RedirectUri)
            {
                if (this.PasswordCryptoProvider.ValidateHash(cipher.Password, client.ClientSecret))
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.Basic,
                                new SentinelClaim(JwtClaimType.Name, client.ClientId),
                                new SentinelClaim(ClaimTypes.NameIdentifier, client.ClientId),
                                new SentinelClaim(ClaimType.Client, client.ClientId),
                                new SentinelClaim(ClaimType.RedirectUri, client.RedirectUri),
                                new SentinelClaim(ClaimType.AuthenticationSource, "local"),
                                new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.Basic)));

                    if (principal.Identity.IsAuthenticated)
                    {
                        client.LastUsed = DateTimeOffset.UtcNow;
                        await this.ClientRepository.Update(client.GetIdentifier(), client);

                        return(principal);
                    }
                }
            }

            return(SentinelPrincipal.Anonymous);
        }
Exemple #18
0
        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);
        }
Exemple #19
0
        /// <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));
        }
Exemple #20
0
        public async void GetAccessToken_WhenGivenValidAuthorizationCodeAndOpenIdScope_ShouldReturnValidIdToken(string clientId, string clientSecret, string redirectUri)
        {
            var code = string.Empty;

            // Get authorization code
            using (var browser = new BrowserSession())
            {
                var url = $"{this.client.BaseAddress}oauth/authorize?response_type=code&client_id={clientId}&redirect_uri={redirectUri}&scope=openid";

                browser.Visit(url);

                Console.WriteLine("Opened authorize page: {0}", url);

                browser.FillIn("Username").With("user");
                browser.FillIn(GrantType.Password).With("user");
                browser.ClickButton("Login");

                Console.WriteLine("Signing in");
                browser.HasContent("The application NUnit wants to access your account", new Options()
                {
                    RetryInterval = TimeSpan.FromSeconds(1)
                });

                browser.ClickButton("Allow");

                Console.WriteLine("Accepting authorization");
                await Task.Delay(TimeSpan.FromSeconds(5));

                var uri = browser.Location;
                Console.WriteLine("Query String: {0}", uri.Query);

                Assert.Contains("code", uri.ParseQueryString().AllKeys);

                code = uri.ParseQueryString()["code"];
            }

            var request = new HttpRequestMessage(HttpMethod.Post, "oauth/token");

            request.Headers.Authorization = new BasicAuthenticationHeaderValue(clientId, clientSecret);
            request.Content = new FormUrlEncodedContent(new Dictionary <string, string>()
            {
                { "grant_type", GrantType.AuthorizationCode },
                { "redirect_uri", redirectUri },
                { "code", code }
            });

            Console.WriteLine("Request: {0}{1}", this.client.BaseAddress, request.RequestUri);

            var response = await this.client.SendAsync(request);

            var content = await response.Content.ReadAsStringAsync();

            var accessTokenResponse = JsonConvert.DeserializeObject <AccessTokenResponse>(content);

            Console.WriteLine("Response: [{0} {1}] {2}", (int)response.StatusCode, response.StatusCode, await response.Content.ReadAsStringAsync());

            var jwt       = new JsonWebToken(accessTokenResponse.IdToken);
            var principal = new SentinelPrincipal(jwt.ToIdentity());

            Console.WriteLine($"Header: {JsonConvert.SerializeObject(jwt.Header)}");
            Console.WriteLine($"Payload: {JsonConvert.SerializeObject(jwt.Payload)}");

            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            Assert.IsNotNullOrEmpty(accessTokenResponse.IdToken);
            Assert.AreEqual("user", jwt.Payload.Subject);
            Assert.IsTrue(principal.Scopes.Count(x => x == "openid") == 1, "The openid scope was included more than once or not at all");

            Assert.IsTrue(jwt.ValidateAuthorizationCode(code), "The authentication code is invalid for this JWT");
            Assert.IsTrue(jwt.ValidateAccessToken(accessTokenResponse.AccessToken), "The access token is invalid for this JWT");

            //Assert.IsTrue(jwt.ValidateSignature(clientSecret));
        }
        /// <summary>Authenticate the user using an API key.</summary>
        /// <param name="digest">The digest.</param>
        /// <returns>The user principal.</returns>
        public override async Task <ISentinelPrincipal> AuthenticateUserWithApiKeyAsync(BasicAuthenticationDigest digest)
        {
            BasicAuthenticationCipher cipher;
            string data;
            string signature;

            try
            {
                // Extract data from digest password
                cipher = digest.GetCipher();

                if (string.IsNullOrEmpty(cipher.ClientId) || string.IsNullOrEmpty(cipher.RedirectUri) || string.IsNullOrEmpty(cipher.Password))
                {
                    return(SentinelPrincipal.Anonymous);
                }


                data      = this.PasswordCryptoProvider.CreateHash(256);
                signature = this.AsymmetricCryptoProvider.Sign(data, cipher.Password);
            }
            catch (ArgumentException)
            {
                return(SentinelPrincipal.Anonymous);
            }
            catch (FormatException)
            {
                return(SentinelPrincipal.Anonymous);
            }

            // Validate client
            var client = await this.ClientRepository.GetClient(cipher.ClientId);

            if (client == null || !client.Enabled || client.RedirectUri != cipher.RedirectUri)
            {
                return(SentinelPrincipal.Anonymous);
            }

            // Validate password
            var userKeys = await this.UserApiKeyRepository.GetForUser(digest.UserId);

            var matchingKey = this.ValidateApiKey(userKeys, data, signature);

            if (matchingKey != null)
            {
                var user = await this.UserRepository.GetUser(matchingKey.UserId);

                if (user != null && user.Enabled)
                {
                    var principal =
                        new SentinelPrincipal(
                            new SentinelIdentity(
                                AuthenticationType.Signature,
                                new SentinelClaim(JwtClaimType.Name, user.UserId),
                                new SentinelClaim(ClaimTypes.NameIdentifier, user.UserId),
                                new SentinelClaim(ClaimTypes.AuthenticationMethod, AuthenticationMethod.Basic),
                                new SentinelClaim(ClaimType.AuthenticationSource, "local"),
                                new SentinelClaim(JwtClaimType.GivenName, user.FirstName),
                                new SentinelClaim(JwtClaimType.FamilyName, user.LastName)));

                    user.LastLogin = DateTimeOffset.UtcNow;
                    await this.UserRepository.Update(user.GetIdentifier(), user);

                    matchingKey.LastUsed = DateTimeOffset.UtcNow;
                    await this.UserApiKeyRepository.Update(matchingKey.GetIdentifier(), matchingKey);

                    return(principal);
                }
            }

            return(SentinelPrincipal.Anonymous);
        }