/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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; }
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); }
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); }
/// <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>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); }
/// <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); }
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); }
/// <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)); }
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); }