Beispiel #1
0
        public async Task <IActionResult> Logout()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

            await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);

            var logoutUrl = $"{Configuration.GetNonEmptyValue("Keycloak:Authority")}/protocol/openid-connect/logout";

            var forwardedHost = HttpContext.Request.Headers.ContainsKey("X-Forwarded-Host")
                ? HttpContext.Request.Headers["X-Forwarded-Host"].ToString()
                : Request.Host.ToString();
            var forwardedPort = HttpContext.Request.Headers["X-Forwarded-Port"];

            //We are always sending X-Forwarded-Port, only time we aren't is when we are hitting the API directly.
            var baseUri = HttpContext.Request.Headers.ContainsKey("X-Forwarded-Host") ? $"{Configuration.GetNonEmptyValue("WebBaseHref")}logout" : "/api";

            var applicationUrl      = $"{XForwardedForHelper.BuildUrlString(forwardedHost, forwardedPort, baseUri)}";
            var keycloakLogoutUrl   = $"{logoutUrl}?post_logout_redirect_uri={applicationUrl}";
            var siteMinderLogoutUrl = $"{Configuration.GetNonEmptyValue("SiteMinderLogoutUrl")}?returl={keycloakLogoutUrl}&retnow=1";

            return(Redirect(siteMinderLogoutUrl));
        }
Beispiel #2
0
        public async Task <IActionResult> RequestCivilFileAccess([FromBody] RequestCivilFileAccess request)
        {
            if (string.IsNullOrEmpty(request.FileId) || string.IsNullOrEmpty(request.UserId))
            {
                return(BadRequest());
            }

            if (!User.IsServiceAccountUser())
            {
                return(Forbid());
            }

            var agencyId = string.IsNullOrEmpty(request.AgencyId) ? "" : AesGcmEncryption.Encrypt(request.AgencyId);
            var partId   = string.IsNullOrEmpty(request.PartId) ? "" : AesGcmEncryption.Encrypt(request.PartId);

            var expiryMinutes = float.Parse(Configuration.GetNonEmptyValue("RequestCivilFileAccessMinutes"));
            await Db.RequestFileAccess.AddAsync(new RequestFileAccess
            {
                FileId    = request.FileId,
                UserId    = request.UserId,
                UserName  = request.UserName,
                AgencyId  = agencyId,
                PartId    = partId,
                Requested = DateTimeOffset.Now,
                Expires   = DateTimeOffset.Now.AddMinutes(expiryMinutes)
            });

            await Db.SaveChangesAsync();

            var forwardedHost = Request.Headers["X-Forwarded-Host"];
            var forwardedPort = Request.Headers["X-Forwarded-Port"];
            var baseUrl       = Configuration.GetNonEmptyValue("WebBaseHref");

            return(Ok(new
            {
                Url = $"{XForwardedForHelper.BuildUrlString(forwardedHost, forwardedPort, baseUrl)}civil-file/{request.FileId}?fromA2A=true"
            }));
        }
Beispiel #3
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            var baseUrl = Configuration.GetNonEmptyValue("WebBaseHref");

            app.Use((context, next) =>
            {
                context.Request.EnableBuffering();
                context.Request.Scheme = "https";
                if (context.Request.Headers.ContainsKey("X-Forwarded-Host") && !env.IsDevelopment())
                {
                    context.Request.PathBase = new PathString(baseUrl.Remove(baseUrl.Length - 1));
                }
                return(next());
            });

            app.UseForwardedHeaders();
            app.UseCors();

            app.UseSwagger(options =>
            {
                options.RouteTemplate = "api/swagger/{documentname}/swagger.json";
                options.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
                {
                    if (!httpReq.Headers.ContainsKey("X-Forwarded-Host"))
                    {
                        return;
                    }

                    var forwardedHost  = httpReq.Headers["X-Forwarded-Host"];
                    var forwardedPort  = httpReq.Headers["X-Forwarded-Port"];
                    swaggerDoc.Servers = new List <OpenApiServer>
                    {
                        new OpenApiServer {
                            Url = XForwardedForHelper.BuildUrlString(forwardedHost, forwardedPort, baseUrl)
                        }
                    };
                });
            });

            app.UseSwaggerUI(options =>
            {
                options.SwaggerEndpoint("swagger/v1/swagger.json", "SCV.API");
                options.RoutePrefix = "api";
            });

            app.UseMiddleware(typeof(ErrorHandlingMiddleware));

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
Beispiel #4
0
        public static IServiceCollection AddScvAuthentication(this IServiceCollection services,
                                                              IWebHostEnvironment env, IConfiguration configuration)
        {
            var baseUrl = configuration.GetNonEmptyValue("WebBaseHref");

            services.AddAuthentication(options =>
            {
                options.DefaultScheme             = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme    = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie(options =>
            {
                options.Cookie.Name = "SCV";
                if (env.IsDevelopment())
                {
                    options.Cookie.Name += ".Development";
                }
                options.Cookie.HttpOnly     = true;
                options.Cookie.SameSite     = SameSiteMode.None;
                options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
                options.Events = new CookieAuthenticationEvents
                {
                    OnRedirectToAccessDenied = context =>
                    {
                        context.Response.StatusCode = StatusCodes.Status403Forbidden;
                        return(context.Response.CompleteAsync());
                    },
                    OnValidatePrincipal = async cookieCtx =>
                    {
                        if (cookieCtx.Principal.Identity.AuthenticationType ==
                            SiteMinderAuthenticationHandler.SiteMinder)
                        {
                            return;
                        }

                        var accessTokenExpiration = DateTimeOffset.Parse(cookieCtx.Properties.GetTokenValue("expires_at"));
                        var timeRemaining         = accessTokenExpiration.Subtract(DateTimeOffset.UtcNow);
                        var refreshThreshold      = TimeSpan.Parse(configuration.GetNonEmptyValue("TokenRefreshThreshold"));

                        if (timeRemaining > refreshThreshold)
                        {
                            return;
                        }

                        var refreshToken      = cookieCtx.Properties.GetTokenValue("refresh_token");
                        var httpClientFactory = cookieCtx.HttpContext.RequestServices.GetRequiredService <IHttpClientFactory>();
                        var httpClient        = httpClientFactory.CreateClient(nameof(CookieAuthenticationEvents));
                        var response          = await httpClient.RequestRefreshTokenAsync(new RefreshTokenRequest
                        {
                            Address      = configuration.GetNonEmptyValue("Keycloak:Authority") + "/protocol/openid-connect/token",
                            ClientId     = configuration.GetNonEmptyValue("Keycloak:Client"),
                            ClientSecret = configuration.GetNonEmptyValue("Keycloak:Secret"),
                            RefreshToken = refreshToken
                        });

                        if (response.IsError)
                        {
                            cookieCtx.RejectPrincipal();
                            await cookieCtx.HttpContext.SignOutAsync(CookieAuthenticationDefaults
                                                                     .AuthenticationScheme);
                        }
                        else
                        {
                            var expiresInSeconds = response.ExpiresIn;
                            var updatedExpiresAt = DateTimeOffset.UtcNow.AddSeconds(expiresInSeconds);
                            cookieCtx.Properties.UpdateTokenValue("expires_at", updatedExpiresAt.ToString());
                            cookieCtx.Properties.UpdateTokenValue("refresh_token", response.RefreshToken);

                            // Indicate to the cookie middleware that the cookie should be remade (since we have updated it)
                            cookieCtx.ShouldRenew = true;
                        }
                    }
                };
            }
                       )
            .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
            {
                options.SignInScheme                  = CookieAuthenticationDefaults.AuthenticationScheme;
                options.Authority                     = configuration.GetNonEmptyValue("Keycloak:Authority");
                options.ClientId                      = configuration.GetNonEmptyValue("Keycloak:Client");
                options.ClientSecret                  = configuration.GetNonEmptyValue("Keycloak:Secret");
                options.RequireHttpsMetadata          = true;
                options.GetClaimsFromUserInfoEndpoint = true;
                options.ResponseType                  = OpenIdConnectResponseType.Code;
                options.UsePkce      = true;
                options.SaveTokens   = true;
                options.CallbackPath = "/api/auth/signin-oidc";
                options.Scope.Add("groups");
                options.Scope.Add("vc_authn");
                options.Events = new OpenIdConnectEvents
                {
                    OnTicketReceived = context =>
                    {
                        context.Properties.Items.Remove(".Token.id_token");
                        context.Properties.Items.Remove(".Token.access_token");
                        context.Properties.Items[".TokenNames"] = "refresh_token;token_type;expires_at";
                        return(Task.CompletedTask);
                    },
#pragma warning disable 1998
                    OnTokenValidated = async context =>
#pragma warning restore 1998
                    {
                        if (!(context.Principal.Identity is ClaimsIdentity identity))
                        {
                            return;
                        }

                        var loggerFactory = context.HttpContext.RequestServices.GetRequiredService <ILoggerFactory>();
                        var logger        = loggerFactory.CreateLogger("OnTokenValidated");
                        logger.LogInformation($"OpenIdConnect UserId - { context.Principal.UserId() } - logged in.");

                        //Cleanup keycloak claims, that are unused.
                        foreach (var claim in identity.Claims.WhereToList(c =>
                                                                          !CustomClaimTypes.UsedKeycloakClaimTypes.Contains(c.Type)))
                        {
                            identity.RemoveClaim(claim);
                        }

                        var applicationCode = "SCV";
                        var partId          = configuration.GetNonEmptyValue("Request:PartId");
                        var agencyId        = configuration.GetNonEmptyValue("Request:AgencyIdentifierId");
                        var isSupremeUser   = false;
                        if (context.Principal.IsVcUser())
                        {
                            var db         = context.HttpContext.RequestServices.GetRequiredService <ScvDbContext>();
                            var userId     = context.Principal.UserId();
                            var now        = DateTimeOffset.Now;
                            var fileAccess = await db.RequestFileAccess
                                             .Where(r => r.UserId == userId && r.Expires > now)
                                             .OrderByDescending(x => x.Id)
                                             .FirstOrDefaultAsync();

                            if (fileAccess != null && !string.IsNullOrEmpty(fileAccess.PartId) && !string.IsNullOrEmpty(fileAccess.AgencyId))
                            {
                                logger.LogInformation($"UserId - { context.Principal.UserId() } - Using credentials passed in from A2A.");
                                var aesGcmEncryption = context.HttpContext.RequestServices.GetRequiredService <AesGcmEncryption>();
                                partId          = aesGcmEncryption.Decrypt(fileAccess.PartId);
                                agencyId        = aesGcmEncryption.Decrypt(fileAccess.AgencyId);
                                applicationCode = "A2A";
                            }
                        }
                        else if (context.Principal.IsIdirUser() && context.Principal.Groups().Contains("court-viewer-supreme"))
                        {
                            isSupremeUser = true;
                        }

                        var claims = new List <Claim>();
                        claims.AddRange(new[] {
                            new Claim(CustomClaimTypes.ApplicationCode, applicationCode),
                            new Claim(CustomClaimTypes.JcParticipantId, partId),
                            new Claim(CustomClaimTypes.JcAgencyCode, agencyId),
                            new Claim(CustomClaimTypes.IsSupremeUser, isSupremeUser.ToString())
                        });

                        identity.AddClaims(claims);
                    },
                    OnRedirectToIdentityProvider = context =>
                    {
                        if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                        {
                            if (!context.Request.Path.StartsWithSegments("/api/auth/login"))
                            {
                                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                                context.HandleResponse();
                                return(Task.CompletedTask);
                            }
                        }

                        context.ProtocolMessage.SetParameter("kc_idp_hint",
                                                             context.Request.Query["redirectUri"].ToString().Contains("fromA2A=true")
                                ? configuration.GetNonEmptyValue("Keycloak:VcIdpHint")
                                : "idir");

                        context.ProtocolMessage.SetParameter("pres_req_conf_id", configuration.GetNonEmptyValue("Keycloak:PresReqConfId"));
                        if (context.HttpContext.Request.Headers["X-Forwarded-Host"].Count > 0)
                        {
                            var forwardedHost = context.HttpContext.Request.Headers["X-Forwarded-Host"];
                            var forwardedPort = context.HttpContext.Request.Headers["X-Forwarded-Port"];
                            context.ProtocolMessage.RedirectUri =
                                $"{XForwardedForHelper.BuildUrlString(forwardedHost, forwardedPort, baseUrl)}{options.CallbackPath}";
                        }
                        return(Task.CompletedTask);
                    }
                };
            })