/// <summary> /// Represents an event called for each request to the authorization endpoint /// to determine if the request is valid and should continue. /// </summary> /// <param name="context">The context instance associated with this event.</param> public override async Task ValidateAuthorizationRequest(ValidateAuthorizationRequestContext context) { // Note: the OpenID Connect server middleware supports the authorization code, implicit and hybrid flows // but this authorization provider only accepts response_type=code authorization/authentication requests. // You may consider relaxing it to support the implicit or hybrid flows. In this case, consider adding // checks rejecting implicit/hybrid authorization requests when the client is a confidential application. if (!context.Request.IsAuthorizationCodeFlow()) { context.Reject( error: OpenIdConnectConstants.Errors.UnsupportedResponseType, description: "Only the authorization code flow is supported by this authorization server."); return; } // Note: to support custom response modes, the OpenID Connect server middleware doesn't // reject unknown modes before the ApplyAuthorizationResponse event is invoked. // To ensure invalid modes are rejected early enough, a check is made here. if (!context.Request.ResponseMode.IsNullOrWhiteSpace() && !context.Request.IsFormPostResponseMode() && !context.Request.IsFragmentResponseMode() && !context.Request.IsQueryResponseMode()) { context.Reject( error: OpenIdConnectConstants.Errors.InvalidRequest, description: "The specified 'response_mode' is unsupported."); return; } // Retrieve the application details corresponding to the requested client_id. var rockContext = new RockContext(); var authClientService = new AuthClientService(rockContext); var authClient = await authClientService.GetByClientIdAsync(context.ClientId); if (authClient == null) { context.Reject( error: OpenIdConnectConstants.Errors.InvalidClient, description: "The specified client identifier is invalid."); return; } if (!context.RedirectUri.IsNullOrWhiteSpace() && !string.Equals(context.RedirectUri, authClient.RedirectUri, StringComparison.OrdinalIgnoreCase)) { context.Reject( error: OpenIdConnectConstants.Errors.InvalidClient, description: "The specified 'redirect_uri' is invalid."); return; } context.Validate(authClient.RedirectUri); }
/// <summary> /// Gets the authentication client. /// </summary> /// <returns></returns> private async Task <AuthClient> GetAuthClient() { if (_authClient == null) { var rockContext = new RockContext(); var authClientService = new AuthClientService(rockContext); var authClientId = PageParameter(PageParamKey.ClientId); _authClient = await authClientService.GetByClientIdAsync(authClientId); } return(_authClient); }
/// <summary> /// Gets the authentication client. /// </summary> /// <returns></returns> private AuthClient GetAuthClient() { if (_authClient == null) { var rockContext = new RockContext(); var authClientService = new AuthClientService(rockContext); var authClientId = PageParameter(PageParamKey.ClientId); _authClient = authClientService.GetByClientId(authClientId); } return(_authClient); }
private void AcceptAuthorization() { var owinContext = Context.GetOwinContext(); var request = owinContext.GetOpenIdConnectRequest(); // TODO: only allow valid scopes. var requestedScopes = request.GetScopes(); var authClientId = PageParameter(PageParamKey.ClientId); IDictionary <string, string> clientAllowedClaims = null; AuthClient authClient = null; IEnumerable <string> clientAllowedScopes = null; using (var rockContext = new RockContext()) { var authClientService = new AuthClientService(rockContext); authClient = authClientService.GetByClientId(authClientId); clientAllowedScopes = RockIdentityHelper.NarrowRequestedScopesToApprovedScopes(rockContext, authClientId, requestedScopes); clientAllowedClaims = RockIdentityHelper.GetAllowedClientClaims(rockContext, authClientId, clientAllowedScopes); } if (authClient == null || clientAllowedScopes == null || clientAllowedClaims == null) { // TODO: Error return; } // Create a new ClaimsIdentity containing the claims that // will be used to create an id_token, a token or a code. var identity = RockIdentityHelper.GetRockClaimsIdentity(CurrentUser, clientAllowedClaims, authClientId); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(identity, new AuthenticationProperties()); // We should set the scopes to the requested valid scopes. ticket.SetScopes(requestedScopes); // Set the resource servers the access token should be issued for. ticket.SetResources("resource_server"); // Returning a SignInResult will ask ASOS to serialize the specified identity // to build appropriate tokens. You should always make sure the identities // you return contain the OpenIdConnectConstants.Claims.Subject claim. In this sample, // the identity always contains the name identifier returned by the external provider. owinContext.Authentication.SignIn(ticket.Properties, identity); }
/// <summary> /// Represents an event called for each request to the token endpoint /// to determine if the request is valid and should continue. /// </summary> /// <param name="context">The context instance associated with this event.</param> public override async Task ValidateTokenRequest(ValidateTokenRequestContext context) { // Note: the OpenID Connect server middleware supports authorization code, refresh token, client credentials // and resource owner password credentials grant types but this authorization provider uses a safer policy // rejecting the last two ones. You may consider relaxing it to support the ROPC or client credentials grant types. if (!context.Request.IsAuthorizationCodeGrantType() && !context.Request.IsRefreshTokenGrantType() && !context.Request.IsTokenRequest()) { context.Reject( error: OpenIdConnectConstants.Errors.UnsupportedGrantType, description: "Only authorization code and refresh token grant types " + "are accepted by this authorization server."); return; } // Note: client authentication is not mandatory for non-confidential client applications like mobile apps // (except when using the client credentials grant type) but this authorization server uses a safer policy // that makes client authentication mandatory and returns an error if client_id or client_secret is missing. // You may consider relaxing it to support the resource owner password credentials grant type // with JavaScript or desktop applications, where client credentials cannot be safely stored. // In this case, call context.Skip() to inform the server middleware the client is not trusted. if (context.ClientId.IsNullOrWhiteSpace() || context.ClientSecret.IsNullOrWhiteSpace()) { context.Reject( error: OpenIdConnectConstants.Errors.InvalidRequest, description: "The mandatory 'client_id'/'client_secret' parameters are missing."); return; } // Retrieve the application details corresponding to the requested client_id. var rockContext = new RockContext(); var authClientService = new AuthClientService(rockContext); var authClient = await authClientService.GetByClientIdAndSecretAsync(context.ClientId, context.ClientSecret); if (authClient == null) { context.Reject( error: OpenIdConnectConstants.Errors.InvalidClient, description: "The specified client credentials are invalid."); return; } context.Validate(); }
/// <summary> /// Shows the detail. /// </summary> /// <param name="clientId">The rest user identifier.</param> public void ShowDetail(int clientId) { var rockContext = new RockContext(); AuthClient authClient = null; var isNew = clientId.Equals(0); if (!isNew) { authClient = new AuthClientService(rockContext).Get(clientId); lTitle.Text = ActionTitle.Edit("Client").FormatAsHtmlTitle(); tbClientSecret.Text = CLIENT_SECRET_PLACE_HOLDER; } else { lTitle.Text = ActionTitle.Add("Client").FormatAsHtmlTitle(); tbClientSecret.Text = string.Empty; } if (authClient == null) { if (!isNew) { DisplayErrorMessage("The Auth Client with the specified Id was found."); return; } authClient = new AuthClient { Id = 0, IsActive = true }; } hfRestUserId.Value = authClient.Id.ToString(); tbName.Text = authClient.Name; cbActive.Checked = authClient.IsActive; tbClientId.Text = authClient.ClientId; tbRedirectUri.Text = authClient.RedirectUri; tbPostLogoutRedirectUri.Text = authClient.PostLogoutRedirectUri; SetClaimsCheckboxValues(authClient.AllowedClaims.FromJsonOrNull <List <string> >()); var editAllowed = authClient.IsAuthorized(Authorization.EDIT, CurrentPerson); lbSave.Visible = editAllowed; }
/// <summary> /// Gets the allowed client scopes. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="clientId">The client identifier.</param> /// <returns></returns> /// <exception cref="ArgumentException"> /// </exception> public static IEnumerable <string> GetAllowedClientScopes(RockContext rockContext, string clientId) { if (rockContext == null) { throw new ArgumentException($"{nameof( rockContext )} cannot be null."); } if (clientId.IsNullOrWhiteSpace()) { throw new ArgumentException($"{nameof( clientId )} cannot be null or empty."); } // The OpenId is required and should always be allowed. var emptyScopeList = new List <string> { }; var authClientService = new AuthClientService(rockContext); var enabledClientScopes = authClientService .Queryable() .Where(ac => ac.ClientId == clientId) .Select(ac => ac.AllowedScopes) .FirstOrDefault(); if (enabledClientScopes.IsNullOrWhiteSpace()) { return(emptyScopeList); } var parsedClientScopes = enabledClientScopes.FromJsonOrNull <List <string> >(); if (parsedClientScopes == null) { return(emptyScopeList); } var activeClientScopes = new AuthScopeService(rockContext) .Queryable() .Where(s => s.IsActive) .Select(s => s.Name); return(parsedClientScopes.Intersect(activeClientScopes)); }
/// <summary> /// Handles the Delete event of the gUserLogins control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs" /> instance containing the event data.</param> protected void gAuthClients_Delete(object sender, RowEventArgs e) { bool canEdit = IsUserAuthorized(Authorization.EDIT); if (canEdit) { using (var rockContext = new RockContext()) { var authClientService = new AuthClientService(rockContext); var authScope = authClientService.Get(e.RowKeyId); if (authScope != null) { authClientService.Delete(authScope); rockContext.SaveChanges(); } } } BindGrid(); }
/// <summary> /// Represents an event called for each request to the logout endpoint /// to determine if the request is valid and should continue. /// </summary> /// <param name="context">The context instance associated with this event.</param> public override async Task ValidateLogoutRequest(ValidateLogoutRequestContext context) { // When provided, post_logout_redirect_uri must exactly // match the address registered by the client application. if (!context.PostLogoutRedirectUri.IsNullOrWhiteSpace()) { var rockContext = new RockContext(); var authClientService = new AuthClientService(rockContext); var authClient = await authClientService.GetByPostLogoutRedirectUrlAsync(context.PostLogoutRedirectUri); if (authClient == null) { context.Reject( error: OpenIdConnectConstants.Errors.InvalidRequest, description: "The specified 'post_logout_redirect_uri' is invalid."); return; } } context.Validate(); }
/// <summary> /// Gets the requested scopes. /// </summary> /// <returns></returns> private List <string> GetRequestedScopes() { var scopes = new List <string> { "Authorization" }; var owinContext = Context.GetOwinContext(); var request = owinContext.GetOpenIdConnectRequest(); var requestedScopes = request.GetScopes(); var rockContext = new RockContext(); var authClientService = new AuthClientService(rockContext); var clientAllowedClaims = authClientService .Queryable() .Where(ac => ac.ClientId == request.ClientId) .Select(ac => ac.AllowedClaims).FirstOrDefault(); var parsedAllowedClientClaims = clientAllowedClaims.FromJsonOrNull <List <string> >(); if (parsedAllowedClientClaims == null) { return(new List <string>()); } var authClaimService = new AuthClaimService(rockContext); var activeAllowedClientClaims = authClaimService .Queryable() .Where(ac => parsedAllowedClientClaims.Contains(ac.Name)) .Where(ac => ac.IsActive) .Where(ac => requestedScopes.Contains(ac.Scope.Name)) .Select(ac => new { Scope = ac.Scope.PublicName, Claim = ac.PublicName }) .GroupBy(ac => ac.Scope, ac => ac.Claim) .ToList() .Select(ac => new { Scope = ac.Key, Claims = string.Join(", ", ac.ToArray()) }); scopes.AddRange(activeAllowedClientClaims.Select(ac => ac.Scope == ac.Claims ? ac.Scope : ac.Scope + " (" + ac.Claims + ")")); return(scopes); //return scopeString.SplitDelimitedValues().ToList(); }
/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { var rockContext = new RockContext(); var authClientService = new AuthClientService(rockContext); var authClientQuery = authClientService.Queryable().AsNoTracking(); if (tbName.Text.IsNotNullOrWhiteSpace()) { authClientQuery = authClientQuery.Where(s => s.Name.Contains(tbName.Text)); } if (ddlActiveFilter.SelectedIndex > -1) { switch (ddlActiveFilter.SelectedValue) { case "active": authClientQuery = authClientQuery.Where(s => s.IsActive); break; case "inactive": authClientQuery = authClientQuery.Where(s => !s.IsActive); break; } } // Sort var sortProperty = gAuthClients.SortProperty; if (sortProperty == null) { sortProperty = new SortProperty(new GridViewSortEventArgs("Name", SortDirection.Ascending)); } authClientQuery = authClientQuery.Sort(sortProperty); gAuthClients.SetLinqDataSource(authClientQuery); gAuthClients.DataBind(); }
/// <summary> /// Gets the allowed client claims. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="clientId">The client identifier.</param> /// <param name="allowedClientScopes">The allowed client scopes.</param> /// <returns></returns> /// <exception cref="ArgumentException"> /// </exception> public static IDictionary <string, string> GetAllowedClientClaims(RockContext rockContext, string clientId, IEnumerable <string> allowedClientScopes) { if (rockContext == null) { throw new ArgumentException($"{nameof( rockContext )} cannot be null."); } if (clientId.IsNullOrWhiteSpace()) { throw new ArgumentException($"{nameof( clientId )} cannot be null or empty."); } var allowedClaimList = new Dictionary <string, string>(); var authClientService = new AuthClientService(rockContext); var allowedClaims = authClientService.Queryable().Where(ac => ac.ClientId == clientId).Select(ac => ac.AllowedClaims).FirstOrDefault(); if (allowedClaims.IsNullOrWhiteSpace()) { return(allowedClaimList); } var parsedClaims = allowedClaims.FromJsonOrNull <List <string> >(); if (parsedClaims == null) { return(allowedClaimList); } return(new AuthClaimService(rockContext) .Queryable() .Where(ac => parsedClaims.Contains(ac.Name)) .Where(ac => ac.IsActive) .Where(ac => allowedClientScopes.Contains(ac.Scope.Name)) .Where(ac => ac.Scope.IsActive) .ToDictionary(vc => vc.Name, vc => vc.Value)); }
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientId; string clientSecret; if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) { context.TryGetFormCredentials(out clientId, out clientSecret); } if (string.IsNullOrWhiteSpace(clientId)) { context.SetError("invalid_clientId", "client_id is not set."); await Task.FromResult<object>(null); return; } //TODO: get authClient (application) from db in future var authClient = new AuthClientService().Get(clientId); // auth client is null if (authClient == null) { context.SetError("invalid_clientId", "client_id is not valid."); await Task.FromResult<object>(null); return; } // authclient is enabled if (!authClient.Enabled) { context.SetError("invalid_clientId", "client_id is not valid."); await Task.FromResult<object>(null); return; } // make sure secret isn't null or empty if (string.IsNullOrWhiteSpace(clientSecret)) { context.SetError("invalid_clientId", "Client secret should be sent."); await Task.FromResult<object>(null); return; } // make sure secret matches if (clientSecret != authClient.Base64Secret) { context.SetError("invalid_clientId", "Client secret is invalid."); await Task.FromResult<object>(null); return; } context.OwinContext.Set("authClient", authClient); context.Validated(); await Task.FromResult<object>(null); }
/// <summary> /// Calls when a process requests authorization. /// </summary> /// <param name="actionContext">The action context, which encapsulates information for using <see cref="T:System.Web.Http.Filters.AuthorizationFilterAttribute" />.</param> public override void OnAuthorization(HttpActionContext actionContext) { // See if user is logged in var principal = System.Threading.Thread.CurrentPrincipal; if (principal != null && principal.Identity != null && !string.IsNullOrWhiteSpace(principal.Identity.Name)) { actionContext.Request.SetUserPrincipal(principal); return; } // If check if ASOS authentication occurred. principal = actionContext.RequestContext.Principal; if (principal != null && principal.Identity != null) { var claimIdentity = principal.Identity as ClaimsIdentity; if (claimIdentity != null) { var clientId = claimIdentity.Claims.FirstOrDefault(c => c.Type == Claims.ClientId)?.Value; if (clientId.IsNotNullOrWhiteSpace()) { using (var rockContext = new RockContext()) { var authClientService = new AuthClientService(rockContext); var authClient = authClientService.GetByClientId(clientId); if (authClient.AllowUserApiAccess) { var userName = claimIdentity.Claims.FirstOrDefault(c => c.Type == Claims.Username)?.Value; if (userName.IsNotNullOrWhiteSpace() && clientId.IsNotNullOrWhiteSpace()) { UserLogin userLogin = null; var userLoginService = new UserLoginService(rockContext); userLogin = userLoginService.GetByUserName(userName); if (userLogin != null) { var identity = new GenericIdentity(userLogin.UserName); principal = new GenericPrincipal(identity, null); actionContext.Request.SetUserPrincipal(principal); return; } } } } } } } // If not, see if there's a valid Rock APIKey token TryRetrieveHeader(actionContext, HeaderTokens.AuthorizationToken, out var authToken); if (string.IsNullOrWhiteSpace(authToken)) { string queryString = actionContext.Request.RequestUri.Query; authToken = System.Web.HttpUtility.ParseQueryString(queryString).Get("apikey"); } if (!string.IsNullOrWhiteSpace(authToken)) { var userLoginService = new UserLoginService(new Rock.Data.RockContext()); var userLogin = userLoginService.Queryable().Where(u => u.ApiKey == authToken).FirstOrDefault(); if (userLogin != null) { var identity = new GenericIdentity(userLogin.UserName); principal = new GenericPrincipal(identity, null); actionContext.Request.SetUserPrincipal(principal); return; } } // If still not successful, check for a JSON Web Token if (TryRetrieveHeader(actionContext, HeaderTokens.JWT, out var jwtString)) { // If the JSON Web Token is in the header, we can determine the User from that var userLogin = JwtHelper.GetUserLoginByJSONWebToken(new RockContext(), jwtString); if (userLogin != null) { var identity = new GenericIdentity(userLogin.UserName); principal = new GenericPrincipal(identity, null); actionContext.Request.SetUserPrincipal(principal); return; } // Just in rare case the GetPersonFromJWTPersonSearchKey feature is being used, see if person can be determined this way var person = JwtHelper.GetPersonFromJWTPersonSearchKey(jwtString); if (person != null) { actionContext.Request.Properties.Add("Person", person); return; } } }
/// <summary> /// Saves the authentication client. /// </summary> /// <param name="authScopeId">The authentication scope identifier.</param> private void SaveAuthClient(int authScopeId) { var isNew = authScopeId.Equals(0); var authClient = new AuthClient(); var editAllowed = authClient.IsAuthorized(Authorization.EDIT, CurrentPerson); if (!editAllowed) { DisplayErrorMessage("The current user is not authorized to make changes."); return; } var rockContext = new RockContext(); var authClientService = new AuthClientService(rockContext); if (isNew) { authClientService.Add(authClient); } else { authClient = authClientService.Get(authScopeId); } if (authClient == null) { DisplayErrorMessage("The Auth Client with the specified Id was found."); return; } if (tbClientSecret.Text.IsNullOrWhiteSpace()) { DisplayErrorMessage("A Client Secret is required."); return; } authClient.Name = tbName.Text; authClient.IsActive = cbActive.Checked; authClient.ClientId = tbClientId.Text; authClient.RedirectUri = tbRedirectUri.Text; authClient.PostLogoutRedirectUri = tbPostLogoutRedirectUri.Text; if (tbClientSecret.Text != CLIENT_SECRET_PLACE_HOLDER) { var entityTypeName = EntityTypeCache.Get <Rock.Security.Authentication.Database>().Name; var databaseAuth = AuthenticationContainer.GetComponent(entityTypeName) as Rock.Security.Authentication.Database; var encryptedClientSecret = databaseAuth.EncryptString(tbClientSecret.Text); authClient.ClientSecretHash = encryptedClientSecret; } var activeClaims = GetActiveClaims(rockContext).Select(ac => ac.ScopeName).Distinct(); var selectedClaims = new List <string>(activeClaims.Count()); var selectedScopes = new List <string>(activeClaims.Count()); foreach (var scope in activeClaims) { var checkboxList = litClaims.FindControl(scope) as RockCheckBoxList; if (checkboxList == null) { continue; } var selectedScopeClaims = checkboxList.SelectedValues; selectedClaims.AddRange(selectedScopeClaims); if (selectedScopeClaims.Any()) { selectedScopes.Add(scope); } } authClient.AllowedClaims = selectedClaims.ToJson(); authClient.AllowedScopes = selectedScopes.ToJson(); rockContext.SaveChanges(); }
/// <summary> /// Calls when a process requests authorization. /// </summary> /// <param name="actionContext">The action context, which encapsulates information for using <see cref="T:System.Web.Http.Filters.AuthorizationFilterAttribute" />.</param> public override void OnAuthorization(HttpActionContext actionContext) { // See if user is logged in var principal = System.Threading.Thread.CurrentPrincipal; if (principal != null && principal.Identity != null && !string.IsNullOrWhiteSpace(principal.Identity.Name)) { actionContext.Request.SetUserPrincipal(principal); return; } // If check if ASOS authentication occurred. principal = actionContext.RequestContext.Principal; if (principal != null && principal.Identity != null) { var claimIdentity = principal.Identity as ClaimsIdentity; if (claimIdentity != null) { var clientId = claimIdentity.Claims.FirstOrDefault(c => c.Type == Claims.ClientId)?.Value; if (clientId.IsNotNullOrWhiteSpace()) { using (var rockContext = new RockContext()) { var authClientService = new AuthClientService(rockContext); var authClient = authClientService.GetByClientId(clientId); if (authClient.AllowUserApiAccess) { var userName = claimIdentity.Claims.FirstOrDefault(c => c.Type == Claims.Username)?.Value; if (userName.IsNotNullOrWhiteSpace() && clientId.IsNotNullOrWhiteSpace()) { UserLogin userLogin = null; var userLoginService = new UserLoginService(rockContext); userLogin = userLoginService.GetByUserName(userName); if (userLogin != null) { var identity = new GenericIdentity(userLogin.UserName); principal = new GenericPrincipal(identity, null); actionContext.Request.SetUserPrincipal(principal); return; } } } } } } } // If not, see if there's a valid token TryRetrieveHeader(actionContext, HeaderTokens.AuthorizationToken, out var authToken); if (string.IsNullOrWhiteSpace(authToken)) { string queryString = actionContext.Request.RequestUri.Query; authToken = System.Web.HttpUtility.ParseQueryString(queryString).Get("apikey"); } if (!string.IsNullOrWhiteSpace(authToken)) { var userLoginService = new UserLoginService(new Rock.Data.RockContext()); var userLogin = userLoginService.Queryable().Where(u => u.ApiKey == authToken).FirstOrDefault(); if (userLogin != null) { var identity = new GenericIdentity(userLogin.UserName); principal = new GenericPrincipal(identity, null); actionContext.Request.SetUserPrincipal(principal); return; } } // If still not successful, check for a JSON Web Token if (TryRetrieveHeader(actionContext, HeaderTokens.JWT, out var jwtString)) { Person person = null; // We need to wait for the JwtHelper.GetPerson method rather than using the await keyword. The await keyword // forces this entire method to be async causing the Secured attribute to process before everything // is finished here Task.Run(async() => { person = await JwtHelper.GetPerson(jwtString); }).Wait(); if (person != null) { actionContext.Request.Properties.Add("Person", person); return; } } }