/// <summary> /// Generates the OpenID Connect compliant Keycloak logout URL /// </summary> /// <param name="parameters"></param> /// <param name="baseUri"></param> /// <param name="redirectUri"></param> /// <returns></returns> public static async Task <Uri> GenerateLogoutUriAsync(IKeycloakParameters parameters, Uri baseUri, Uri redirectUri) { if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } if (baseUri == null) { throw new ArgumentNullException(nameof(baseUri)); } if (redirectUri == null) { throw new ArgumentNullException(nameof(redirectUri)); } // Generate logout URI and data var uriManager = await OidcDataManager.GetCachedContextAsync(parameters); var logoutParams = uriManager.BuildEndSessionEndpointContent(baseUri, null, redirectUri.ToString()); var logoutUrl = uriManager.GetEndSessionEndpoint(); // Return logout URI var logoutQueryString = await logoutParams.ReadAsStringAsync(); return(new Uri(logoutUrl + (!string.IsNullOrEmpty(logoutQueryString) ? "?" + logoutQueryString : ""))); }
/// <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); }
/// <summary> /// Validates an IKeycloakParameters object for completeness and correctness /// </summary> /// <param name="parameters"></param> /// <returns></returns> public static void ValidateParameters(IKeycloakParameters parameters) { if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } // Verify required parameters if (parameters.KeycloakUrl == null) { throw new ArgumentNullException(nameof(parameters.KeycloakUrl)); } if (parameters.Realm == null) { throw new ArgumentNullException(nameof(parameters.Realm)); } // Set default parameters if (string.IsNullOrWhiteSpace(parameters.ResponseType)) { throw new ArgumentNullException(nameof(parameters.ResponseType)); } if (string.IsNullOrWhiteSpace(parameters.Scope)) { throw new ArgumentNullException(nameof(parameters.Scope)); } if (string.IsNullOrWhiteSpace(parameters.CallbackPath)) { throw new ArgumentNullException(nameof(parameters.CallbackPath)); } // Validate other parameters if (!Uri.IsWellFormedUriString(parameters.KeycloakUrl, UriKind.Absolute)) { throw new ArgumentException(nameof(parameters.KeycloakUrl)); } if (!Uri.IsWellFormedUriString(parameters.CallbackPath, UriKind.Relative) && parameters.CallbackPath != Constants.KeycloakParameters.NoCallbackUri) { throw new ArgumentException(nameof(parameters.CallbackPath)); } if (parameters.PostLogoutRedirectUrl != null && !Uri.IsWellFormedUriString(parameters.PostLogoutRedirectUrl, UriKind.RelativeOrAbsolute)) { throw new ArgumentException(nameof(parameters.PostLogoutRedirectUrl)); } var logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); // Attempt to refresh OIDC metadata from endpoint (on separate thread) try { Task.Run(() => OidcDataManager.GetCachedContextAsync(parameters)).Wait(); } catch (Exception exception) { logger.Error($"Invalid Keycloak server parameters specified: See inner for server error: {exception.InnerException}"); throw new ArgumentException("Invalid Keycloak server parameters specified: See inner for server error", exception); } }
private async Task <string> ExecuteHttpRequestAsync() { var uriManager = OidcDataManager.GetCachedContext(Options); var response = await SendHttpPostRequest(uriManager.GetTokenEndpoint(), uriManager.BuildAccessTokenEndpointContent(Request.Uri, AuthResponse.Code)); return(await response.Content.ReadAsStringAsync()); }
private async Task <string> ExecuteHttpRequestAsync() { var uriManager = await OidcDataManager.GetCachedContextAsync(Context, Options); var response = await SendHttpPostRequest(uriManager.GetTokenEndpoint(), uriManager.BuildRefreshTokenEndpointContent(RefreshToken)); return(await response.Content.ReadAsStringAsync()); }
/// <summary> /// Generates the local URL on which to accept OIDC callbacks from Keycloak /// </summary> /// <param name="parameters"></param> /// <param name="baseUri"></param> /// <returns></returns> public static async Task <Uri> GenerateLoginCallbackUriAsync(IKeycloakParameters parameters, Uri baseUri) { if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } if (baseUri == null) { throw new ArgumentNullException(nameof(baseUri)); } return((await OidcDataManager.GetCachedContextAsync(parameters)).GetCallbackUri(baseUri)); }
protected async Task CopyFromJwt(string accessToken, string refreshToken = null, string idToken = null) { if (accessToken == null) { throw new ArgumentException(nameof(accessToken)); } // Validate JWTs provided var tokenHandler = new KeycloakTokenHandler(); var uriManager = await OidcDataManager.GetCachedContextAsync(_parameters); SecurityToken accessSecurityToken, idSecurityToken = null, refreshSecurityToken = null; if (_parameters.UseRemoteTokenValidation) { accessSecurityToken = await KeycloakTokenHandler.ValidateTokenRemote(accessToken, uriManager); } else { accessSecurityToken = tokenHandler.ValidateToken(accessToken, _parameters, uriManager); } // Double-check if (accessSecurityToken == null) { throw new Exception("Internal error: Invalid access token; valid required"); } if (idToken != null) { idSecurityToken = tokenHandler.ValidateToken(idToken, _parameters, uriManager); } if (refreshToken != null) { refreshSecurityToken = tokenHandler.ValidateToken(refreshToken, _parameters, uriManager); } // Save to this object // TODO: Convert to MS claims parsing in token handler _kcClaims = GenerateJwtClaims(accessSecurityToken as JwtSecurityToken, idSecurityToken as JwtSecurityToken, refreshSecurityToken as JwtSecurityToken); _idToken = idSecurityToken as JwtSecurityToken; _accessToken = accessSecurityToken as JwtSecurityToken; _refreshToken = refreshSecurityToken as JwtSecurityToken; }
/// <summary> /// Generates the OpenID Connect compliant Keycloak login URL /// </summary> /// <param name="parameters"></param> /// <param name="baseUri"></param> /// <param name="state"></param> /// <returns></returns> public static async Task <Uri> GenerateLoginUriAsync(IKeycloakParameters parameters, Uri baseUri, string state = null) { if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } if (baseUri == null) { throw new ArgumentNullException(nameof(baseUri)); } // Generate login URI and data var uriManager = await OidcDataManager.GetCachedContextAsync(parameters); var loginParams = uriManager.BuildAuthorizationEndpointContent(baseUri, state ?? Guid.NewGuid().ToString()); var loginUrl = uriManager.GetAuthorizationEndpoint(); // Return login URI var loginQueryString = await loginParams.ReadAsStringAsync(); return(new Uri(loginUrl + (!string.IsNullOrEmpty(loginQueryString) ? "?" + loginQueryString : ""))); }
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)); }