protected OidcDataManager(IKeycloakParameters options)
        {
            _options = options;
            _nextCachedRefreshTime = DateTime.Now;

            Authority = _options.KeycloakUrl + "/realms/" + _options.Realm;
            MetadataEndpoint = new Uri(Authority + "/" + OpenIdProviderMetadataNames.Discovery);
            TokenValidationEndpoint = new Uri(Authority + "/tokens/validate");
        }
 public bool TryValidateToken(string jwt, IKeycloakParameters options, OidcDataManager uriManager, out SecurityToken rToken)
 {
     try
     {
         rToken = ValidateToken(jwt, options, uriManager);
         return true;
     }
     catch (Exception)
     {
         rToken = null;
         return false;
     }
 }
        public SecurityToken ValidateToken(string jwt, IKeycloakParameters options, OidcDataManager uriManager)
        {
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateLifetime = true,
                RequireExpirationTime = true,
                ValidateIssuer = !options.DisableIssuerValidation,
                ValidateAudience = !options.DisableAudienceValidation,
                ValidateIssuerSigningKey = !options.DisableTokenSignatureValidation,
                RequireSignedTokens = !options.AllowUnsignedTokens,
                ValidIssuer = uriManager.GetIssuer(),
                ClockSkew = options.TokenClockSkew,
                ValidAudiences = new List<string> {"null", options.ClientId},
                IssuerSigningTokens = uriManager.GetJsonWebKeys().GetSigningTokens(),
                AuthenticationType = options.AuthenticationType // Not used
            };

            return ValidateToken(jwt, tokenValidationParameters);
        }
 public static async Task<OidcDataManager> CreateCachedContext(IKeycloakParameters options,
     bool preload = true)
 {
     var newContext = new OidcDataManager(options);
     OidcManagerCache[options.AuthenticationType + CachedContextPostfix] = newContext;
     if (preload) await newContext.ValidateCachedContextAsync();
     return newContext;
 }
 /// <summary>
 ///     Load a new Keycloak-based identity from a claims identity
 /// </summary>
 /// <param name="parameters"></param>
 protected KeycloakIdentity(IKeycloakParameters parameters)
 {
     if (parameters == null) throw new ArgumentNullException(nameof(parameters));
     ValidateParameters(parameters);
     _parameters = parameters;
 }
        /// <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));

            // Attempt to refresh OIDC metadata from endpoint (on separate thread)
            try
            {
                Task.Run(() => OidcDataManager.GetCachedContextAsync(parameters)).Wait();
            }
            catch (Exception exception)
            {
                throw new ArgumentException("Invalid Keycloak server parameters specified: See inner for server error",
                    exception);
            }
        }
 /// <summary>
 ///     Trys to validate an IKeycloakParameters object for completeness and correctness
 /// </summary>
 /// <param name="parameters"></param>
 /// <returns></returns>
 public static bool TryValidateParameters(IKeycloakParameters parameters)
 {
     try
     {
         ValidateParameters(parameters);
         return true;
     }
     catch (Exception)
     {
         return false;
     }
 }
        /// <summary>
        ///     Generates the OpenID Connect compliant Keycloak logout URL
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="baseUri"></param>
        /// <param name="redirectUrl"></param>
        /// <returns></returns>
        public static async Task<Uri> GenerateLogoutUriAsync(IKeycloakParameters parameters, Uri baseUri,
            string redirectUrl = null)
        {
            if (parameters == null) throw new ArgumentNullException(nameof(parameters));
            if (baseUri == null) throw new ArgumentNullException(nameof(baseUri));

            // Generate logout URI and data
            var uriManager = await OidcDataManager.GetCachedContextAsync(parameters);
            var logoutParams = uriManager.BuildEndSessionEndpointContent(baseUri, null, redirectUrl);
            var logoutUrl = uriManager.GetEndSessionEndpoint();

            // Return logout URI
            var logoutQueryString = await logoutParams.ReadAsStringAsync();
            return new Uri(logoutUrl + (!string.IsNullOrEmpty(logoutQueryString) ? "?" + logoutQueryString : ""));
        }
 public async Task<SecurityToken> ValidateTokenAsync(string jwt, IKeycloakParameters options)
 {
     var uriManager = await OidcDataManager.GetCachedContextAsync(options);
     return ValidateToken(jwt, options, uriManager);
 }
 public static OidcDataManager GetCachedContext(IKeycloakParameters options)
 {
     return GetCachedContext(options.AuthenticationType);
 }
        /// <summary>
        ///     Converts a set of JWTs into a Keycloak identity
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="accessToken"></param>
        /// <param name="refreshToken"></param>
        /// <param name="idToken"></param>
        /// <returns></returns>
        public static async Task<KeycloakIdentity> ConvertFromJwtAsync(IKeycloakParameters parameters,
            string accessToken, string refreshToken = null, string idToken = null)
        {
            if (parameters == null) throw new ArgumentNullException(nameof(parameters));
            if (accessToken == null) throw new ArgumentNullException(nameof(accessToken));

            var kcIdentity = new KeycloakIdentity(parameters);
            try
            {
                await kcIdentity.CopyFromJwt(accessToken, refreshToken, idToken);
            }
            catch (SecurityTokenExpiredException)
            {
                // Load new identity from token endpoint via refresh token (if possible)
                await kcIdentity.RefreshIdentity(refreshToken);
            }
            return kcIdentity;
        }
        /// <summary>
        ///     Converts a JWT token-response endpoint message into a Keycloak identity
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="response"></param>
        /// <param name="baseUri"></param>
        /// <returns></returns>
        public static async Task<KeycloakIdentity> ConvertFromAuthResponseAsync(IKeycloakParameters parameters,
            AuthorizationResponse response, Uri baseUri)
        {
            if (parameters == null) throw new ArgumentNullException(nameof(parameters));
            if (response == null) throw new ArgumentNullException(nameof(response));
            if (baseUri == null) throw new ArgumentNullException(nameof(baseUri));

            response.ThrowIfError();
            var message = new RequestAccessTokenMessage(baseUri, parameters, response);
            return await ConvertFromTokenResponseAsync(parameters, await message.ExecuteAsync());
        }
 /// <summary>
 ///     Converts a JWT token-response endpoint message into a Keycloak identity
 /// </summary>
 /// <param name="parameters"></param>
 /// <param name="message"></param>
 /// <returns></returns>
 public static Task<KeycloakIdentity> ConvertFromTokenResponseAsync(IKeycloakParameters parameters,
     TokenResponse message)
 {
     if (parameters == null) throw new ArgumentNullException(nameof(parameters));
     if (message == null) throw new ArgumentNullException(nameof(message));
     return ConvertFromJwtAsync(parameters, message.AccessToken, message.RefreshToken, message.IdToken);
 }
        /// <summary>
        ///     Converts a keycloak-generated claims list into a Keycloak identity
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="claims"></param>
        /// <returns></returns>
        public static Task<KeycloakIdentity> ConvertFromClaimsAsync(IKeycloakParameters parameters,
            IEnumerable<Claim> claims)
        {
            if (parameters == null) throw new ArgumentNullException(nameof(parameters));
            if (claims == null) throw new ArgumentNullException(nameof(claims));

            var claimLookup = claims.ToLookup(c => c.Type, c => c.Value);
            var idToken = claimLookup[Constants.ClaimTypes.IdToken].FirstOrDefault();
            var accessToken = claimLookup[Constants.ClaimTypes.AccessToken].FirstOrDefault();
            var refreshToken = claimLookup[Constants.ClaimTypes.RefreshToken].FirstOrDefault();

            return ConvertFromJwtAsync(parameters, accessToken, refreshToken, idToken);
        }
 /// <summary>
 ///     Converts a keycloak-generated claims identity into a Keycloak identity
 /// </summary>
 /// <param name="parameters"></param>
 /// <param name="identity"></param>
 /// <returns></returns>
 public static Task<KeycloakIdentity> ConvertFromClaimsIdentityAsync(IKeycloakParameters parameters,
     ClaimsIdentity identity)
 {
     if (parameters == null) throw new ArgumentNullException(nameof(parameters));
     if (identity == null) throw new ArgumentNullException(nameof(identity));
     return ConvertFromClaimsAsync(parameters, identity.Claims);
 }
 public static Task ValidateCachedContextAsync(IKeycloakParameters options)
 {
     var context = GetCachedContext(options.AuthenticationType);
     return context.ValidateCachedContextAsync();
 }
        /// <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 Task<OidcDataManager> GetCachedContextAsync(IKeycloakParameters options)
 {
     var context = GetCachedContextSafe(options.AuthenticationType);
     return context != null ? Task.FromResult(context) : CreateCachedContext(options);
 }
        /// <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);
        }