private static void ValidateOptions() { // Load web root path from config if (string.IsNullOrWhiteSpace(Options.VirtualDirectory)) { Options.VirtualDirectory = "/"; } Options.VirtualDirectory = NormalizeUrl(Options.VirtualDirectory); // Set default options if (string.IsNullOrWhiteSpace(Options.ResponseType)) { Options.ResponseType = "code"; } if (string.IsNullOrWhiteSpace(Options.Scope)) { Options.Scope = "openid"; } if (string.IsNullOrWhiteSpace(Options.CallbackPath)) { Options.CallbackPath = $"{Options.VirtualDirectory}/owin/security/keycloak/{Uri.EscapeDataString(Options.AuthenticationScheme)}/callback"; } if (string.IsNullOrWhiteSpace(Options.PostLogoutRedirectUrl)) { Options.PostLogoutRedirectUrl = Options.VirtualDirectory; } if (Options.SignInAsAuthenticationSchema == null) { try { //Options.SignInAsAuthenticationType = App.GetDefaultSignInAsAuthenticationType(); Options.SignInAsAuthenticationSchema = ""; } catch (Exception) { Options.SignInAsAuthenticationSchema = ""; } } // Switch composite options if (Options.EnableWebApiMode) { Options.EnableBearerTokenAuth = true; Options.ForceBearerTokenAuth = true; } // Validate other options if (Options.ForceBearerTokenAuth && !Options.EnableBearerTokenAuth) { Options.EnableBearerTokenAuth = true; } Options.KeycloakUrl = NormalizeUrl(Options.KeycloakUrl); Options.CallbackPath = NormalizeUrlPath(Options.CallbackPath); // Final parameter validation KeycloakIdentity.ValidateParameters(Options); }
private async Task LogoutRedirectAsync(AuthenticationProperties properties) { // Redirect response to logout Response.Redirect( (await KeycloakIdentity.GenerateLogoutUriAsync(Options, Request.Uri, new Uri(properties.RedirectUri))) .ToString()); }
private async Task ValidateSignInAsIdentities() { foreach (var origIdentity in Context.Authentication.User.Identities) { try { if (!origIdentity.HasClaim(Constants.ClaimTypes.AuthenticationType, Options.AuthenticationType)) { continue; } var kcIdentity = await KeycloakIdentity.ConvertFromClaimsIdentityAsync(Options, origIdentity); if (!kcIdentity.IsTouched) { continue; } // Replace identity if expired var identity = await kcIdentity.ToClaimsIdentityAsync(); Context.Authentication.User = new ClaimsPrincipal(identity); SignInAsAuthentication(identity, null, Options.SignInAsAuthenticationType); } catch (AuthenticationException) { Context.Authentication.SignOut(origIdentity.AuthenticationType); } catch (Exception) { // TODO: Some kind of exception logging, maybe log the user out throw; } } }
public override async Task <bool> HandleRequestAsync() { // Check SignInAs identity for authentication update if (Context.User.Identity.IsAuthenticated) { await ValidateSignInAsIdentities(); } var currUri = new Uri(CurrentUri); // Check for valid callback URI var callbackUri = await KeycloakIdentity.GenerateLoginCallbackUriAsync(Options, currUri); if (!Options.ForceBearerTokenAuth && currUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped) == callbackUri.ToString()) { // Create authorization result from query var authResult = new AuthorizationResponse(currUri.Query); try { // Validate passed state var stateData = ReturnState(authResult.State); if (stateData == null) { throw new Exception("Invalid state: Please reattempt the request"); } // Parse properties from state data AuthenticationProperties properties; if (stateData.TryGetValue(Constants.CacheTypes.AuthenticationProperties, out object authpprop)) { properties = authpprop as AuthenticationProperties ?? new AuthenticationProperties(); } else { properties = new AuthenticationProperties(); } // Process response var kcIdentity = await KeycloakIdentity.ConvertFromAuthResponseAsync(Options, authResult, currUri); var identity = await kcIdentity.ToClaimsIdentityAsync(); Context.User.AddIdentity(identity); SignInAsAuthentication(identity, properties, Options.SignInAsAuthenticationSchema); // Redirect back to the original secured resource, if any if (!string.IsNullOrWhiteSpace(properties.RedirectUri) && Uri.IsWellFormedUriString(properties.RedirectUri, UriKind.Absolute)) { Response.Redirect(properties.RedirectUri); return(true); } } catch (Exception exception) { await GenerateErrorResponseAsync(HttpStatusCode.BadRequest, "Bad Request", exception.Message); return(true); } } return(false); }
private async Task LogoutRedirectAsync() { // Redirect response to logout var temp = (await KeycloakIdentity.GenerateLogoutUriAsync(Options, Request.Uri)).ToString(); Response.Redirect(temp); }
/// <summary> /// Method logs generates the logout url for the realm and performs a user log out /// and issues a redirection to the login page for the user to enter credentials again. /// </summary> /// <param name="identity">Current identity signed in keycloak. It will be forced log out.</param> /// <returns>Redirection to login page</returns> private async Task ForceLogoutRedirectAsync(ClaimsIdentity identity) { // generate logout uri var uri = await KeycloakIdentity.GenerateLogoutUriAsync(Options, Request.Uri); //foreach (var claim in identity.Claims) //{ // _logger.Debug($"ForceLogoutRedirectAsync user claim {claim.Type} - {claim.Value}"); //} _logger.Debug($"Force logout identity with isAuthenticated:{identity.IsAuthenticated}"); Claim firstOrDefault = identity.Claims.FirstOrDefault(claim => claim.Type == "refresh_token"); if (firstOrDefault != null) { await OidcDataManager.HttpLogoutPost(firstOrDefault.Value, Options, uri); } //redirect to relogin var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode); if (challenge == null) { _logger.Debug($"Force logged out {identity.Name}.Challenge is null.Return."); return; } _logger.Debug($"Force logged out {identity.Name}.Redirecting from challenge properties."); await LoginRedirectAsync(challenge.Properties); }
private async Task LogoutRedirectAsync() { // Redirect response to logout Response.Redirect( (await KeycloakIdentity.GenerateLogoutUriAsync(Context, Options, Request.Uri)) .ToString()); }
public override async Task <bool> InvokeAsync() { // Check SignInAs identity for authentication update if (Context.Authentication.User.Identity.IsAuthenticated) { await ValidateSignInAsIdentities(); } // Check for valid callback URI var callbackUri = await KeycloakIdentity.GenerateLoginCallbackUriAsync(Options, Request.Uri); if (!Options.ForceBearerTokenAuth && Request.Uri.GetLeftPart(UriPartial.Path) == callbackUri.ToString()) { // Create authorization result from query var authResult = new AuthorizationResponse(Request.Uri.Query); try { // Validate passed state var stateData = Global.StateCache.ReturnState(authResult.State); if (stateData == null) { throw new BadRequestException("Invalid state: Please reattempt the request"); } // Parse properties from state data var properties = stateData[Constants.CacheTypes.AuthenticationProperties] as AuthenticationProperties ?? new AuthenticationProperties(); // Process response var kcIdentity = await KeycloakIdentity.ConvertFromAuthResponseAsync(Options, authResult, Request.Uri); var identity = await kcIdentity.ToClaimsIdentityAsync(); Context.Authentication.User.AddIdentity(identity); SignInAsAuthentication(identity, properties, Options.SignInAsAuthenticationType); // Redirect back to the original secured resource, if any if (!string.IsNullOrWhiteSpace(properties.RedirectUri) && Uri.IsWellFormedUriString(properties.RedirectUri, UriKind.Absolute)) { Response.Redirect(properties.RedirectUri); return(true); } } catch (BadRequestException exception) { await GenerateErrorResponseAsync(HttpStatusCode.BadRequest, "Bad Request", exception.Message); return(true); } } return(false); }
private async Task LoginRedirectAsync(AuthenticationProperties properties) { if (string.IsNullOrEmpty(properties.RedirectUri)) { properties.RedirectUri = Request.Uri.ToString(); } // Create state var stateData = new Dictionary <string, object> { { Constants.CacheTypes.AuthenticationProperties, properties } }; var state = Global.StateCache.CreateState(stateData); // Redirect response to login Response.Redirect((await KeycloakIdentity.GenerateLoginUriAsync(Options, Request.Uri, state)).ToString()); }
public override async Task <AuthenticationTicket> ExecuteAsync() { // Validate passed state var stateData = Global.StateCache.ReturnState(AuthResponse.State); if (stateData == null) { throw new BadRequestException("Invalid state: Please reattempt the request"); } // Generate claims and create user information & authentication ticket var kcIdentity = new KeycloakIdentity(await ExecuteHttpRequestAsync()); var properties = stateData[Constants.CacheTypes.AuthenticationProperties] as AuthenticationProperties ?? new AuthenticationProperties(); return(new AuthenticationTicket(await kcIdentity.ValidateIdentity(Options), properties)); }
protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Bearer token authentication override if (Options.EnableBearerTokenAuth) { // Try to authenticate via bearer token auth if (Request.Headers.ContainsKey(Constants.BearerTokenHeader)) { var bearerAuthArr = Request.Headers[Constants.BearerTokenHeader].Trim().Split(new[] { ' ' }, 2); if ((bearerAuthArr.Length == 2) && bearerAuthArr[0].ToLowerInvariant() == "bearer") { try { var authResponse = new TokenResponse(bearerAuthArr[1], null, null); var kcIdentity = await KeycloakIdentity.ConvertFromTokenResponseAsync(Options, authResponse); var identity = await kcIdentity.ToClaimsIdentityAsync(); SignInAsAuthentication(identity, null, Options.SignInAsAuthenticationType); return(new AuthenticationTicket(identity, new AuthenticationProperties())); } catch (Exception) { // ignored } } } // If bearer token auth is forced, skip standard auth if (Options.ForceBearerTokenAuth) { return(null); } } return(null); }
public static async Task <ClaimsIdentity> GetKeycloakIdentityAsync(string username, string password) { if (Options == null) { throw new ArgumentNullException("options"); } if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) { throw new InvalidCredentialException("username or password is empty."); } var uriManager = await OidcDataManager.GetCachedContextAsync(Options); var response = SendHttpPostRequest(uriManager.GetTokenEndpoint(), uriManager.BuildROPCAccessTokenEndpointContent(username, password)); var result = await response.Content.ReadAsStringAsync(); var tokenrespones = new TokenResponse(result); var claimidentity = await KeycloakIdentity.ConvertFromTokenResponseAsync(Options, tokenrespones); var identity = await claimidentity.ToClaimsIdentityAsync(); return(new ClaimsIdentity(identity.Claims, Options.SignInAsAuthenticationSchema, identity.NameClaimType, identity.RoleClaimType)); }
private async Task LogoutRedirectAsync() { // Redirect response to logout Response.Redirect((await KeycloakIdentity.GenerateLogoutUriAsync(Options, new Uri(CurrentUri))).ToString()); }
public override async Task <bool> InvokeAsync() { // Check SignInAs identity for authentication update if (Context.Authentication.User.Identity.IsAuthenticated) { await ValidateSignInAsIdentities(); } // Check for valid callback URI var callbackUri = await KeycloakIdentity.GenerateLoginCallbackUriAsync(Options, Request.Uri); if (!Options.ForceBearerTokenAuth && Request.Uri.GetLeftPart(UriPartial.Path) == callbackUri.ToString()) { // Create authorization result from query var authResult = new AuthorizationResponse(Request.Uri.Query); _logger.Debug($"Request from {Request.Uri}"); // If the authorization response returned a "error" query parameter (instead of "code" + "state"), redirect to a configured URL. // This could occur if the login is aborted by the user. if (!authResult.IsSuccessfulResponse()) { HandleAuthError(authResult); return(true); } try { // Validate passed state var stateData = Global.StateCache.ReturnState(authResult.State) ?? new Dictionary <string, object>(); //throw new BadRequestException("Invalid state: Please reattempt the request"); // Process response and gather claims. If No state is found in cache we will log out the user from Keycloak and redirect him //again for login. StateData must exist and match the oidc_state received from request var kcIdentity = await KeycloakIdentity.ConvertFromAuthResponseAsync(Options, authResult, Request.Uri); var identity = await kcIdentity.ToClaimsIdentityAsync(); if (!stateData.ContainsKey(Constants.CacheTypes.AuthenticationProperties)) { await ForceLogoutRedirectAsync(identity); _logger.Debug($"State data is null.Logging out user and redirecting"); return(true); } // Parse properties from state data var properties = stateData[Constants.CacheTypes.AuthenticationProperties] as AuthenticationProperties; //everything is ok until here, sign in the user Context.Authentication.User = new ClaimsPrincipal(identity); SignInAsAuthentication(identity, properties, Options.SignInAsAuthenticationType); _logger.Debug($"Signed in user {identity.Name} with state data."); // Trigger OnAuthenticated? var eventArgs = new OnAuthenticatedEventArgs { RedirectUri = properties?.RedirectUri }; Options.OnAuthenticated?.Invoke(Context, eventArgs); // Redirect back to the original secured resource, if any if (!string.IsNullOrWhiteSpace(eventArgs.RedirectUri) && Uri.IsWellFormedUriString(eventArgs.RedirectUri, UriKind.Absolute)) { Response.Redirect(eventArgs.RedirectUri); return(true); } } catch (Exception exception) { _logger.Debug($"Returning false for {exception.Message} {exception.StackTrace}"); await GenerateErrorResponseAsync(HttpStatusCode.InternalServerError, "Internal Server Error", exception.Message); return(false); } } return(false); }
private void ValidateOptions() { var logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); // Check to ensure authentication type isn't already used var authType = Options.AuthenticationType; if (!Global.KeycloakOptionStore.TryAdd(authType, Options)) { logger.Error($"KeycloakAuthenticationOptions: Authentication type '{authType}' already used; required unique"); throw new Exception( $"KeycloakAuthenticationOptions: Authentication type '{authType}' already used; required unique"); } // Verify required options if (Options.KeycloakUrl == null) { ThrowOptionNotFound(nameof(Options.KeycloakUrl)); } if (Options.Realm == null) { ThrowOptionNotFound(nameof(Options.Realm)); } // Load web root path from config if (string.IsNullOrWhiteSpace(Options.VirtualDirectory)) { Options.VirtualDirectory = "/"; } Options.VirtualDirectory = NormalizeUrl(Options.VirtualDirectory); if (!Uri.IsWellFormedUriString(Options.VirtualDirectory, UriKind.Relative)) { ThrowInvalidOption(nameof(Options.VirtualDirectory)); } // Set default options if (string.IsNullOrWhiteSpace(Options.ResponseType)) { Options.ResponseType = "code"; } if (string.IsNullOrWhiteSpace(Options.Scope)) { Options.Scope = "openid"; } if (string.IsNullOrWhiteSpace(Options.CallbackPath)) { Options.CallbackPath = $"{Options.VirtualDirectory}/owin/security/keycloak/{Uri.EscapeDataString(Options.AuthenticationType)}/callback"; } if (string.IsNullOrWhiteSpace(Options.PostLogoutRedirectUrl)) { Options.PostLogoutRedirectUrl = Options.VirtualDirectory; } if (Options.SignInAsAuthenticationType == null) { try { Options.SignInAsAuthenticationType = App.GetDefaultSignInAsAuthenticationType(); } catch (Exception) { Options.SignInAsAuthenticationType = ""; } } // Switch composite options if (Options.EnableWebApiMode) { Options.EnableBearerTokenAuth = true; Options.ForceBearerTokenAuth = true; } // Validate other options if (Options.ForceBearerTokenAuth && !Options.EnableBearerTokenAuth) { Options.EnableBearerTokenAuth = true; } Options.KeycloakUrl = NormalizeUrl(Options.KeycloakUrl); Options.CallbackPath = NormalizeUrlPath(Options.CallbackPath); //for more than 2 minutes set default value if (TimeSpan.Compare(Options.RefreshBeforeTokenExpiration.Duration(), TimeSpan.FromSeconds(120).Duration()) > 0) { Options.RefreshBeforeTokenExpiration = TimeSpan.FromSeconds(30); } // Final parameter validation KeycloakIdentity.ValidateParameters(Options); }
public override async Task <ClaimsIdentity> ExecuteAsync() { var newKcIdentity = new KeycloakIdentity(await ExecuteHttpRequestAsync(RefreshToken)); return(await newKcIdentity.ValidateIdentity(Options)); }
private void ValidateOptions() { // Check to ensure authentication type isn't already used var authType = Options.AuthenticationType; if (!Global.KeycloakOptionStore.TryAdd(authType, Options)) { throw new Exception( $"KeycloakAuthenticationOptions: Authentication type '{authType}' already used; required unique"); } // Verify required options if (Options.KeycloakUrl == null) { ThrowOptionNotFound(nameof(Options.KeycloakUrl)); } if (Options.Realm == null) { ThrowOptionNotFound(nameof(Options.Realm)); } // Load web root path from config if (string.IsNullOrWhiteSpace(Options.VirtualDirectory)) { Options.VirtualDirectory = "/"; } Options.VirtualDirectory = NormalizeUrl(Options.VirtualDirectory); if (!Uri.IsWellFormedUriString(Options.VirtualDirectory, UriKind.Relative)) { ThrowInvalidOption(nameof(Options.VirtualDirectory)); } // Set default options if (string.IsNullOrWhiteSpace(Options.ResponseType)) { Options.ResponseType = "code"; } if (string.IsNullOrWhiteSpace(Options.Scope)) { Options.Scope = "openid"; } if (string.IsNullOrWhiteSpace(Options.CallbackPath)) { Options.CallbackPath = $"{Options.VirtualDirectory}/owin/security/keycloak/{Uri.EscapeDataString(Options.AuthenticationType)}/callback"; } if (string.IsNullOrWhiteSpace(Options.PostLogoutRedirectUrl)) { Options.PostLogoutRedirectUrl = Options.VirtualDirectory; } if (Options.SignInAsAuthenticationType == null) { try { Options.SignInAsAuthenticationType = App.GetDefaultSignInAsAuthenticationType(); } catch (Exception) { Options.SignInAsAuthenticationType = ""; } } // Switch composite options if (Options.EnableWebApiMode) { Options.EnableBearerTokenAuth = true; Options.ForceBearerTokenAuth = true; } // Validate other options if (Options.ForceBearerTokenAuth && !Options.EnableBearerTokenAuth) { Options.EnableBearerTokenAuth = true; } Options.KeycloakUrl = NormalizeUrl(Options.KeycloakUrl); Options.CallbackPath = NormalizeUrlPath(Options.CallbackPath); // Final parameter validation KeycloakIdentity.ValidateParameters(Options); }