Esempio n. 1
0
        protected async Task WriteTokenCookies(HttpContext context, List <Claim> claims)
        {
            var domain = CasConfig.BaseDomain(context.Request);

            // write the XSRF-TOKEN cookie (if it will be verified)
            if (CasConfig.VerifyXsrfInHeader || CasConfig.VerifyXsrfInCookie)
            {
                string xsrf   = this.GenerateSafeRandomString(16);
                string signed = xsrf;
                if (!CasConfig.RequireHttpOnlyOnUserCookie)
                {
                    // if the source claim is going to be in a cookie that is readable by JavaScript the XSRF must be signed
                    signed = await TokenIssuer.IssueXsrfToken(xsrf);
                }
                context.Response.Cookies.Append("XSRF-TOKEN", signed, new CookieOptions()
                {
                    HttpOnly = CasConfig.RequireHttpOnlyOnXsrfCookie,
                    Secure   = CasConfig.RequireSecureForCookies,
                    Domain   = domain,
                    SameSite = CasConfig.SameSite,
                    Path     = "/"
                });
                Logger.LogInformation($"wrote XSRF-TOKEN cookie on domain \"{domain}\".");
                claims.Add(new Claim("xsrf", xsrf));
            }

            // write the user cookie
            string jwt = await TokenIssuer.IssueToken(claims);

            Console.WriteLine("jwt....................................jwt");
            Console.WriteLine(jwt);
            Console.WriteLine("jwt....................................jwt");
            var userCookie = CasConfig.UserCookieName;

            context.Response.Cookies.Append(userCookie, jwt, new CookieOptions()
            {
                HttpOnly = CasConfig.RequireHttpOnlyOnUserCookie,
                Secure   = CasConfig.RequireSecureForCookies,
                Domain   = domain,
                SameSite = CasConfig.SameSite,
                Path     = "/"
            });
            Logger.LogInformation($"wrote session cookie as \"{userCookie}\" on domain \"{domain}\".");

            // revoke the authflow cookie
            context.Response.Cookies.Delete("authflow");
        }
Esempio n. 2
0
        public static async Task AddCasServerAuthAsync(this IServiceCollection services)
        {
            // add the logger
            services.AddSingleLineConsoleLogger();

            // add HttpClients
            services.AddHttpClient("netbricks")
            .ConfigurePrimaryHttpMessageHandler(() => new CasProxyHandler());
            services.AddHttpClient("cas")
            .ConfigurePrimaryHttpMessageHandler(() => new CasProxyHandler());

            // add the access token fetcher
            services.AddAccessTokenFetcher();

            // add the configuration service
            services.TryAddSingleton <IConfig, CasConfig>();

            // load the configuration and log it
            using (var provider = services.BuildServiceProvider())
            {
                var logger           = provider.GetService <ILogger <CasServerAuthServicesConfiguration> >();
                var authCodeReceiver = provider.GetService <ICasAuthCodeReceiver>();

                // get the config
                var config = provider.GetService <IConfig>() as CasConfig;
                if (config == null)
                {
                    throw new Exception("AddCasClientAuth: CasConfig could not be found in the IServiceCollection.");
                }

                // determine the authentication type
                var authType = config.AuthType();
                if (authType == AuthTypes.Service)
                {
                    logger.LogInformation("authentication: application ClientId and ClientSecret (service principal).");
                }
                if (authType == AuthTypes.Token)
                {
                    logger.LogInformation("authentication: managed identity with failback to az cli.");
                }

                // load the configuration
                logger.LogInformation("Loading configuration...");
                await config.Apply();

                // confirm and log the configuration
                config.Optional("AUTH_TYPE", authType.ToString());
                config.Optional("AUTH_TYPE_CONFIG", config.AuthType("CONFIG").ToString());
                config.Optional("AUTH_TYPE_VAULT", config.AuthType("VAULT").ToString());
                config.Optional("AUTH_TYPE_GRAPH", config.AuthType("GRAPH").ToString());
                config.Require("AZURE_TENANT_ID", CasConfig.AzureTenantId);
                config.Require("AZURE_CLIENT_ID", CasConfig.AzureClientId);
                if (authType == AuthTypes.Service || authCodeReceiver != null)
                {
                    config.Require("AZURE_CLIENT_SECRET", await config.AzureClientSecret(), hideValue: true);
                }
                else
                {
                    config.Optional("AZURE_CLIENT_SECRET", await config.AzureClientSecret(), hideValue: true);
                }
                config.Optional("APPCONFIG", CasConfig.AppConfig, hideIfEmpty: true);
                config.Optional("CONFIG_KEYS", CasConfig.ConfigKeys, hideIfEmpty: true);
                config.Optional("PROXY", CasConfig.Proxy, hideIfEmpty: true);
                config.Optional("DEFAULT_HOST_URL", CasConfig.DefaultHostUrl, hideIfEmpty: true);
                config.Optional("SERVER_HOST_URL", CasConfig.ServerHostUrl, hideIfEmpty: true);
                config.Optional("CLIENT_HOST_URL", CasConfig.ClientHostUrl, hideIfEmpty: true);
                config.Optional("WEB_HOST_URL", CasConfig.WebHostUrl, hideIfEmpty: true);
                config.Optional("USE_INSECURE_DEFAULTS", CasConfig.UseInsecureDefaults, hideValue: false);
                config.Optional("IS_HTTPS", CasConfig.IsHttps, hideValue: false);
                config.Require("AZURE_AUTHORITY", CasConfig.AzureAuthority);
                config.Optional("GOOGLE_CLIENT_ID", CasConfig.GoogleClientId);
                config.Require("REDIRECT_URI", CasConfig.RedirectUri());
                config.Require("ISSUER", CasConfig.Issuer);
                config.Require("AUDIENCE", CasConfig.Audience);
                config.Require("ALLOWED_ORIGINS", CasConfig.AllowedOrigins);
                config.Require("BASE_DOMAIN", CasConfig.BaseDomain());
                config.Require("PUBLIC_KEYS_URL", CasConfig.PublicKeysUrl);
                config.Require("PRIVATE_KEY", await config.PrivateKey(), hideValue: true);
                config.Require("PRIVATE_KEY_PASSWORD", await config.PrivateKeyPassword(), hideValue: true);
                config.Require("PUBLIC_CERT_0", await config.PublicCert(0), hideValue: true);
                config.Optional("PUBLIC_CERT_1", await config.PublicCert(1), hideValue: true, hideIfEmpty: true);
                config.Optional("PUBLIC_CERT_2", await config.PublicCert(2), hideValue: true, hideIfEmpty: true);
                config.Optional("PUBLIC_CERT_3", await config.PublicCert(3), hideValue: true, hideIfEmpty: true);
                config.Optional("DEFAULT_REDIRECT_URL", CasConfig.DefaultRedirectUrl);
                config.Optional("AZURE_APPLICATION_ID", CasConfig.AzureApplicationIds, hideIfEmpty: true);
                config.Optional("REQUIRE_SECURE_FOR_COOKIES", CasConfig.RequireSecureForCookies, hideValue: false);
                config.Optional("REQUIRE_USER_ENABLED_ON_REISSUE", CasConfig.RequireUserEnabledOnReissue, hideValue: false);
                config.Optional("REQUIRE_HTTPONLY_ON_USER_COOKIE", CasConfig.RequireHttpOnlyOnUserCookie, hideValue: false);
                config.Optional("REQUIRE_HTTPONLY_ON_XSRF_COOKIE", CasConfig.RequireHttpOnlyOnXsrfCookie, hideValue: false);
                config.Optional("VERIFY_XSRF_IN_HEADER", CasConfig.VerifyXsrfInHeader, hideValue: false);
                config.Optional("VERIFY_XSRF_IN_COOKIE", CasConfig.VerifyXsrfInCookie, hideValue: false);
                config.Optional("SAME_SITE", CasConfig.SameSite.ToString());
                config.Optional("USER_COOKIE_NAME", CasConfig.UserCookieName);
                config.Optional("ROLE_FOR_ADMIN", CasConfig.RoleForAdmin);
                config.Optional("ROLE_FOR_SERVICE", CasConfig.RoleForService);
                config.Optional("AZURE_DOMAIN_HINT", CasConfig.AzureDomainHint, hideIfEmpty: true);
                config.Optional("GOOGLE_DOMAIN_HINT", CasConfig.GoogleDomainHint, hideIfEmpty: true);
                config.Optional("GOOGLE_EMAIL_MUST_BE_VERIFIED", CasConfig.GoogleEmailMustBeVerified, hideValue: false);
                config.Optional("JWT_DURATION", CasConfig.JwtDuration.ToString());
                config.Optional("JWT_SERVICE_DURATION", CasConfig.JwtServiceDuration.ToString());
                config.Optional("JWT_MAX_DURATION", CasConfig.JwtMaxDuration.ToString());
                config.Optional("COMMAND_PASSWORD", await config.CommandPassword(), hideValue: true);
            }

            // add the issuer service
            services.AddSingleton <CasTokenIssuer, CasTokenIssuer>();

            // add the IDPs
            services.AddSingleton <ICasIdp, CasAzureAd>();
            if (!string.IsNullOrEmpty(CasConfig.GoogleClientId))
            {
                services.AddSingleton <ICasIdp, CasGoogleId>();
            }

            // setup CORS policy
            if (CasConfig.AllowedOrigins.Length > 0)
            {
                services.AddCors(options =>
                {
                    options.AddPolicy("cas-server", builder =>
                    {
                        builder.WithOrigins(CasConfig.AllowedOrigins)
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials();
                    });
                });
            }
        }
Esempio n. 3
0
        protected override async Task <AuthenticateResult> HandleAuthenticateAsync()
        {
            bool isTokenFromHeader = false;
            bool isTokenFromCookie = false;

            try
            {
                Logger.LogDebug("CasAuthentication: started authentication check...");

                // drop all internal identity headers so they aren't propogated
                Request.Headers.Remove("X-IDENTITY");
                Request.Headers.Remove("X-EMAIL");
                Request.Headers.Remove("X-NAME");
                Request.Headers.Remove("X-ROLES");

                // check first for header
                string token  = string.Empty;
                var    header = Request.Headers["Authorization"];
                if (header.Count() > 0)
                {
                    Logger.LogDebug($"CasAuthentication: checking header named \"Authorization\" for token...");
                    token             = header.First().Replace("Bearer ", "");
                    isTokenFromHeader = true;
                }

                // look next at the cookie
                if (CasConfig.VerifyTokenInCookie && string.IsNullOrEmpty(token))
                {
                    Logger.LogDebug($"CasAuthentication: checking cookie named \"{CasConfig.UserCookieName}\" for token...");
                    token             = Request.Cookies[CasConfig.UserCookieName];
                    isTokenFromCookie = true;
                }

                // shortcut if there is no token
                if (string.IsNullOrEmpty(token))
                {
                    Logger.LogDebug("CasAuthentication: no token was found.");
                    return(AuthenticateResult.NoResult());
                }

                // attempt reissue is appropriate
                if (
                    !string.IsNullOrEmpty(CasConfig.ReissueUrl) && // reissue is allowed (there is a URL)
                    CasTokenValidator.IsTokenExpired(token)     // the token is expired
                    )
                {
                    // attempt to reissue
                    Logger.LogDebug("CasAuthentication: attempted to reissue an expired token...");
                    var httpClientFactory = Request.HttpContext.RequestServices.GetService(typeof(IHttpClientFactory)) as IHttpClientFactory;
                    var httpClient        = httpClientFactory.CreateClient("cas");
                    token = await CasTokenValidator.ReissueToken(httpClient, token);

                    Logger.LogDebug("CasAuthentication: reissued token successfully");

                    // rewrite the cookie
                    if (isTokenFromCookie)
                    {
                        Response.Cookies.Append(CasConfig.UserCookieName, token, new CookieOptions()
                        {
                            HttpOnly = CasConfig.RequireHttpOnlyOnUserCookie,
                            Secure   = CasConfig.RequireSecureForCookies,
                            Domain   = CasConfig.BaseDomain(Request),
                            SameSite = CasConfig.SameSite,
                            Path     = "/"
                        });
                    }
                }

                // validate the token
                var jwt = await this.CasTokenValidator.ValidateToken(token);

                // if the token was in the header and that wasn't allowed, it had better be a service account
                if (isTokenFromHeader &&
                    !CasConfig.VerifyTokenInHeader &&
                    !jwt.Payload.Claims.IsService()
                    )
                {
                    throw new Exception("only service account types are allowed in the header");
                }

                // propogate the claims (this overload uses uri-names and dedupes)
                var claims = new List <Claim>();
                foreach (var claim in jwt.Payload.Claims)
                {
                    claims.AddLong(claim.Type, claim.Value);
                }

                // build the identity, principal, and ticket
                var identity  = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket    = new AuthenticationTicket(principal, Scheme.Name);
                return(AuthenticateResult.Success(ticket));
            }
            catch (Exception e)
            {
                Logger.LogWarning(e, "CasAuthentication: exception...");
                if (isTokenFromCookie)
                {
                    Response.Cookies.Delete(CasConfig.UserCookieName);                    // revoke the cookie
                }
                return(AuthenticateResult.Fail(e));
            }
        }
Esempio n. 4
0
        public static async Task AddCasClientAuthAsync(this IServiceCollection services)
        {
            // add the logger
            services.AddSingleLineConsoleLogger();

            // add the HttpContext
            services.AddHttpContextAccessor();

            // add HttpClients
            services.AddHttpClient("netbricks")
            .ConfigurePrimaryHttpMessageHandler(() => new CasProxyHandler());
            services.AddHttpClient("cas")
            .ConfigurePrimaryHttpMessageHandler(() => new CasProxyHandler());

            // add the access token fetcher
            services.AddAccessTokenFetcher();

            // add the configuration service
            services.TryAddSingleton <IConfig, CasConfig>();

            // load the configuration and log it
            using (var provider = services.BuildServiceProvider())
            {
                var logger = provider.GetService <ILogger <CasClientAuthServicesConfiguration> >();

                // get the config
                var config = provider.GetService <IConfig>() as CasConfig;
                if (config == null)
                {
                    throw new Exception("AddCasClientAuth: CasConfig could not be found in the IServiceCollection.");
                }

                // determine the authentication type
                var authType = config.AuthType();
                if (authType == AuthTypes.Service)
                {
                    logger.LogInformation("authentication: application ClientId and ClientSecret (service principal).");
                }
                if (authType == AuthTypes.Token)
                {
                    logger.LogInformation("authentication: managed identity with failback to az cli.");
                }

                // load the configuration
                logger.LogInformation("Loading configuration...");
                await config.Apply();

                // confirm and log the configuration
                config.Optional("AUTH_TYPE", authType.ToString());
                config.Optional("AUTH_TYPE_CONFIG", config.AuthType("CONFIG").ToString());
                if (authType == AuthTypes.Service)
                {
                    config.Require("AZURE_TENANT_ID", CasConfig.AzureTenantId);
                    config.Require("AZURE_CLIENT_ID", CasConfig.AzureClientId);
                    config.Require("AZURE_CLIENT_SECRET", await config.AzureClientSecret(), hideValue: true);
                }
                config.Optional("APPCONFIG", CasConfig.AppConfig, hideIfEmpty: true);
                config.Optional("CONFIG_KEYS", CasConfig.ConfigKeys, hideIfEmpty: true);
                config.Optional("PROXY", CasConfig.Proxy, hideIfEmpty: true);
                config.Optional("DEFAULT_HOST_URL", CasConfig.DefaultHostUrl, hideIfEmpty: true);
                config.Optional("SERVER_HOST_URL", CasConfig.ServerHostUrl);
                config.Optional("CLIENT_HOST_URL", CasConfig.ClientHostUrl);
                config.Optional("WEB_HOST_URL", CasConfig.WebHostUrl);
                config.Optional("USE_INSECURE_DEFAULTS", CasConfig.UseInsecureDefaults, hideValue: false);
                config.Optional("IS_HTTPS", CasConfig.IsHttps, hideValue: false);
                config.Require("ISSUER", CasConfig.Issuer);
                config.Require("AUDIENCE", CasConfig.Audience);
                config.Require("ALLOWED_ORIGINS", CasConfig.AllowedOrigins);
                config.Require("BASE_DOMAIN", CasConfig.BaseDomain());
                config.Require("WELL_KNOWN_CONFIG_URL", CasConfig.WellKnownConfigUrl);
                config.Optional("REQUIRE_SECURE_FOR_COOKIES", CasConfig.RequireSecureForCookies, hideValue: false);
                config.Optional("REQUIRE_HTTPONLY_ON_USER_COOKIE", CasConfig.RequireHttpOnlyOnUserCookie, hideValue: false);
                config.Optional("REQUIRE_HTTPONLY_ON_XSRF_COOKIE", CasConfig.RequireHttpOnlyOnXsrfCookie, hideValue: false);
                config.Optional("VERIFY_TOKEN_IN_HEADER", CasConfig.VerifyTokenInHeader, hideValue: false);
                config.Optional("VERIFY_TOKEN_IN_COOKIE", CasConfig.VerifyTokenInCookie, hideValue: false);
                config.Optional("VERIFY_XSRF_IN_HEADER", CasConfig.VerifyXsrfInHeader, hideValue: false);
                config.Optional("VERIFY_XSRF_IN_COOKIE", CasConfig.VerifyXsrfInCookie, hideValue: false);
                config.Optional("SAME_SITE", CasConfig.SameSite.ToString());
                config.Optional("USER_COOKIE_NAME", CasConfig.UserCookieName);
                config.Optional("ROLE_FOR_ADMIN", CasConfig.RoleForAdmin);
                config.Optional("ROLE_FOR_SERVICE", CasConfig.RoleForService);
            }

            // add the validator service
            services.AddSingleton <CasTokenValidator>();

            // setup authentication
            services
            .AddAuthentication("cas")
            .AddScheme <CasAuthenticationOptions, CasAuthenticationHandler>("cas", o => new CasAuthenticationOptions());

            // setup authorization
            services.AddSingleton <IAuthorizationHandler, CasXsrfHandler>();
            services.AddAuthorization(options =>
            {
                options.AddPolicy("cas", policy =>
                {
                    policy.AddAuthenticationSchemes("cas");
                    policy.RequireAuthenticatedUser();
                    policy.Requirements.Add(new CasXsrfRequirement());
                });
                options.AddPolicy("cas-no-xsrf", policy =>
                {
                    policy.AddAuthenticationSchemes("cas");
                    policy.RequireAuthenticatedUser();
                });
                options.DefaultPolicy = options.GetPolicy("cas");
            });

            // setup CORS policy
            if (CasConfig.AllowedOrigins?.Length > 0)
            {
                services.AddCors(options =>
                {
                    options.AddPolicy("cas-client", builder =>
                    {
                        builder.WithOrigins(CasConfig.AllowedOrigins)
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials();
                    });
                });
            }
        }