CloudFront Issue loading text, instead showing html - caching

I am using CloudFront with the following policies.
Cache Policy: Disable
Origin Request Policy:
cookies_config {
cookie_behavior = "all"
}
headers_config {
header_behavior = "whitelist"
headers {
items = ["Host", "Accept-language]
}
}
query_strings_config {
query_string_behavior = "all"
}
But the problem is that in one of the section of website showing HTML instead of text/design.
Expected Result
Actual Result

The issue was in the origin request policy of CloudFront, missing some header which I wasn't able to find specific, so putting header as AllViwers, it is showing the expected result.
Origin Request Policy
cookies_config {
cookie_behavior = "all"
}
headers_config {
header_behavior = "allViewer"
}
query_strings_config {
query_string_behavior = "all"
}

Related

KTOR Client and Spring Switchuser

I'm trying to implement a client for spring-security's SwitchUserFilter (server-side). As client I'm using KTOR (with OKHttp inside).
SwitchUserFilter requires me to log in, then drop the Authorization header and use the Cookie tinstead. If I send the Authorization header together with the Cookie header, spring's SecurityContext coming from SwitchUserFilter will be overwritten with my admin user again.
Is there something I can configure in KTOR, so that the [Authorization] header is removed, once I have switched the user?
KTOR has to be setup with two things:
SwitchUserFilter will send a redirect (HTTP 302) that we need to ignore. For this a HttpResponseValidator needs to be configured.
Auth needs to be removed similar to the comment from #Delta_George
HttpClient(OkHttp) {
HttpResponseValidator {
// for 302 don't react - so we can switch user successfully. If we follow, this doesn't work anymore.
validateResponse { response ->
val statusCode = response.status.value
val originCall = response.call
if (statusCode < 300 || originCall.attributes.contains(ValidateMark)) {
return#validateResponse
}
val exceptionCall = originCall.save().apply {
attributes.put(ValidateMark, Unit)
}
val excResp = exceptionCall.response
val excRespTxt = excResp.readText()
when (statusCode) {
302 -> {} // do nothing on "Found" statuscode
in 300..399 -> throw RedirectResponseException(excResp, excRespTxt)
in 400..499 -> throw ClientRequestException(excResp, excRespTxt)
in 500..599 -> throw ServerResponseException(excResp, excRespTxt)
else -> throw ResponseException(excResp, excRespTxt)
}
}
}
... // other configurations
}
and impersonate(...):
suspend fun impersonate(impersonateWithUser: PersonEntity): Impersonation<PersonEntity> {
return runCatching {
val toImpersonate = impersonateWithUser.login.replace(Regex("^\\+"), "%2B")
client.get<HttpResponse>("$BASE_URL/login/impersonate?username=${toImpersonate}") // with baseauth again
}.map {
when (it.status) {
HttpStatusCode.Found -> {
client.feature(Auth)!!.providers.removeAll { true }
Impersonation.ok(impersonateWithUser)
}
else -> Impersonation.failure(impersonateWithUser, it)
}
}.getOrElse {
Log.e(TAG, "impersonate: ", it)
Impersonation.communicationError(impersonateWithUser, it)
}
}
to end the impersonation you call the respective endpoint given in SwitchUserFilter on the serverside.

Why does OIDC login breaks in Edge but not in FireFox?

I am wokring on a website (.NET Framework 4.6.1) and we implemented OIDC authentication (IdentityServer4). The implementation is very basic, nothing fancy just some code challange and token validation. We tested it and it worked real nice on both Edge and FireFox.
Then we were asked to implement "acr_values" parameter for MFA. In the authentication configuration, specifically inside RedirectToIdentityProvider (which is part of Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationNotifications), we add the specified "acr_values" parameter the following way (the value itself is set in a config file, and its similar to "xyz:asd:wtf:qwe"):
n.ProtocolMessage.AcrValues = authCfg.AcrValues
In a very similar setup (by similar i mean almost identical) it is working without any issues. For my setup it only works in Firefox. When trying in Edge we get AuthenticationFailed (which is also a Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationNotifications) with the following error:
2021-05-26 13:00:08.0633 ERROR MT.Translate.Startup
OIDC-Notification: AuthenticationFailed:
2021-05-26 13:00:08.0633 ERROR MT.Translate.Startup Value cannot
be null. Parameter name: s
2021-05-26 13:00:08.0633 ERROR MT.Translate.Startup
-TargetSite-------------------------------
2021-05-26 13:00:08.0633 ERROR MT.Translate.Startup Byte[]
FromBase64String(System.String)
2021-05-26 13:00:08.0633 ERROR MT.Translate.Startup
-Source-----------------------------------
2021-05-26 13:00:08.0633 ERROR MT.Translate.Startup mscorlib
In development enviroment the behaviour is a bit different. We do not get AuthenticationFailed, because after verifying the login information IdentityServer's redirection does nothing, but return us to the same login screen.
To summerize, without "acr:values" MFA was not working, but otherwise it was working in both Edge and Firefox. After implementig "acr_values" Firefox was working with MFA but not in Edge. So we rolled back to the previous version, where we have no "acr_values" and now MFA works with Edge and Firefox too.
The error does not make any sense to me. There is no parameter called "s", at least I have never heard of it in the context of authentication. The fact that without the necessary code it works does not make any sense to me. Also how can it work on Firefox and not on Edge?
Bonus Objective: Only in Edge a png is not appearing. It was not touched and in every other browser it shows up. How and why is my question.
Thank you for reading my post and I am looking forward to any insight what is happening.
Some code snippets:
oicdAuthOpt.Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication && AppSettingsKey.AuthCodeChallangeEnabled.Enabled)
{
// generate code verifier and code challenge
var codeVerifier = CryptoRandom.CreateUniqueId(32);
string codeChallenge;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
codeChallenge = Base64UrlEncoder.Encode(challengeBytes);
}
// set code_challenge parameter on authorization request
n.ProtocolMessage.Parameters.Add("code_challenge", codeChallenge);
n.ProtocolMessage.Parameters.Add("code_challenge_method", "S256");
if (AppSettingsKey.MultiFactorAuthEnabled.Enabled)
n.ProtocolMessage.AcrValues = authCfg.AcrValues ?? n.ProtocolMessage.AcrValues;
// remember code verifier in cookie (adapted from OWIN nonce cookie)
// see: https://github.com/scottbrady91/Blog-Example-Classes/blob/master/AspNetFrameworkPkce/ScottBrady91.BlogExampleCode.AspNetPkce/Startup.cs#L85
RememberCodeVerifier(n, codeVerifier);
}
logger.Debug("OIDC-Notification: RedirectToIdentityProvider Called");
//if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
logger.Debug(" RequestType=" + OpenIdConnectRequestType.Logout);
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
logger.Debug(" IdTokenHint got from n.OwinContext.Authentication.User");
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
logger.Debug(" IdTokenHint=" + n?.ProtocolMessage?.IdTokenHint);
}
return Task.CompletedTask;
},
Code for the IndentityServer is on their github (Quickstart).
For authorization middleware we made a custom System.Web.Mvc.AuthorizeAttribute.
public override void OnAuthorization(AuthorizationContext filterContext)
{
try
{
if (AppSettingsKey.LoginEnabled.Enabled && AppSettingsKey.OpenIdConnectSSOEnabled.Enabled)
{
var cookie = HttpContext.Current.Request.Cookies["oidc.default"];
if (cookie == null)
{
logger.Debug("oidc.default is null -> HandleUnauthorizedRequest");
base.HandleUnauthorizedRequest(filterContext);
}
else
{
if (CookieKeyStore.Instance.CheckIfContains(cookie.Value))
{
if (!CookieKeyStore.Instance.isExpired(cookie.Value))
{
logger.Debug("oidc.default is not expired:" + cookie.Value + " -> OnAuthorization");
//requires oidc.default and ASP.NET_SessionID cookies
base.OnAuthorization(filterContext);
}
else
{
logger.Debug("oidc.default is expired:" + cookie.Value + " -> HandleUnauthorizedRequest");
base.HandleUnauthorizedRequest(filterContext);
}
}
else
{
logger.Debug("insert oidc.default into the KeyStore:" + cookie.Value + " -> OnAuthorization");
CookieKeyStore.Instance.HandleCookies(cookie);
base.OnAuthorization(filterContext);
}
}
}
else
base.OnAuthorization(filterContext);
}
catch (Exception e)
{
logger.Error(e, "Exception while overriding the OnAuthorization method.");
}
}
"oidc.default" is our custom cookie configured into OIDC.
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
CookieName = "oidc.default",
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager(),
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnResponseSignOut = context =>
{
CookieKeyStore.Instance.Clear(context.Request.Cookies["oidc.default"]);
}
}
});

How to set responsive images with CKEditor 5?

Using the Simple Upload Adapter, according to the CKEditor 5 documentation upon a successful image upload, the server should return either:
This for a single image:
{
default: 'http://example.com/images/image–default-size.png'
}
Or this for multiple (i.e. for the srcset attribute)
{
default: 'http://example.com/images/image–default-size.png',
'160': 'http://example.com/images/image–size-160.image.png',
'500': 'http://example.com/images/image–size-500.image.png',
'1000': 'http://example.com/images/image–size-1000.image.png',
'1052': 'http://example.com/images/image–default-size.png'
}
For starters the documentation is incorrect as per this SO post, so I'm not surprised this doesn't work. But does anyone know what response format CKEditor 5 is expecting for it to correctly insert/build the srcset attribute? It does take the default key but seems to ignore the others!
Inside of the _initListeners function of the upload adapter you will find that the Promise only resolves with the following:
resolve( {
default: response.url
} );
The solution - change the Promise to resolve the following:
resolve( response.urls );
Note, in this example the response object may have either keys url or urls.
I ended up using the following as I ignore null keys in my server responses.
if ( response.hasOwnProperty( 'url' ) ) {
resolve( {
default: response.url
} );
} else if ( response.hasOwnProperty( 'urls' ) ) {
resolve( response.urls );
}
As a sidenote, if you've read through the other SO post I referred to, I would also recommend removing this (see commented section):
if ( !response /** || !response.uploaded */ ) {
return reject( response && response.error && response.error.message ? response.error.message : genericError );
}
I'm not a fan of using arbitrary flags in response, if the upload failed then I would rather see a HTTP status code indicating it, I can't see any reason why we need to return 200 and { "uploaded" : false }

Web API add openid scope to auth url for swagger/swashbuckle UI

We have a asp.net web api application which uses swagger/swashbuckle for it's api documentation. The api is secured by azure AD using oauth/openid-connect. The configuration for swagger is done in code:
var oauthParams = new Dictionary<string, string>
{
{ "resource", "https://blahblahblah/someId" }
};
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion(Version, Name);
c.UseFullTypeNameInSchemaIds();
c.OAuth2("oauth2")
.Description("OAuth2 Implicit Grant")
.Flow("implicit")
.AuthorizationUrl(
"https://login.microsoftonline.com/te/ourtenant/ourcustompolicy/oauth2/authorize")
.TokenUrl(
"https://login.microsoftonline.com/te/ourtenant/ourcustompolicy/oauth2/token");
c.OperationFilter<AssignOAuth2SecurityRequirements>();
})
.EnableSwaggerUi(c =>
{
c.EnableOAuth2Support(_applicationId, null, "http://localhost:49919/swagger/ui/o2c-html", "Swagger", " ", oauthParams);
c.BooleanValues(new[] { "0", "1" });
c.DisableValidator();
c.DocExpansion(DocExpansion.List);
});
When swashbuckle constructs the auth url for login, it automatically adds:
&scope=
However I need this to be:
&scope=openid
I have tried adding this:
var oauthParams = new Dictionary<string, string>
{
{ "resource", "https://blahblahblah/someId" },
{ "scope", "openid" }
};
But this then adds:
&scope=&someotherparam=someothervalue&scope=openid
Any ideas how to add
&scope=openid
To the auth url that swashbuckle constructs?
Many thanks
So, found out what the issue was, the offending code can be found here:
https://github.com/swagger-api/swagger-ui/blob/2.x/dist/lib/swagger-oauth.js
These js files are from a git submodule that references the old version of the UI.
I can see on lines 154-158 we have this code:
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes.join(scopeSeparator));
url += '&state=' + encodeURIComponent(state);
It basically adds scopes regardless of whether there are scopes or not. This means you cannot add scopes in the additionalQueryParams dictionary that gets sent into EnableOAuth2Support as you will get a url that contains 2 scope query params i.e.
&scope=&otherparam=otherparamvalue&scope=openid
A simple length check around the scopes would fix it.
I ended up removing swashbuckle from the web api project and added a different nuget package called swagger-net, found here:
https://www.nuget.org/packages/Swagger-Net/
This is actively maintained and it resolved the issue and uses a newer version of the swagger ui. The configuration remained exactly the same, the only thing you need to change is your reply url which is now:
http://your-url/swagger/ui/oauth2-redirect-html

Setting ACL for a Pre Signed Object URL with Fog

I'm generating a fog pre-signed URL for AWS using the following snippet:
bucket = "..."
object = "demo.jpg"
expires = Integer(Time.now + 4.hours)
headers = {}
options = { path_style: true }
fog.put_object_url(bucket, object, expires, headers, options)
This works great - except that the uploaded objects aren't accessible to the public. How can a public-read access control list (ACL) be applied to the upload path?
You have to list these extra parameters (eg. x-amz-acl, Content-Type) under the "query" key of the options hash.
So your example would be.
bucket = "..."
object = "demo.jpg"
expires = Integer(Time.now + 4.hours)
headers = {}
query = {"x-amz-acl" => "public-read"}
options = { path_style: true, query: query }
fog.put_object_url(bucket, object, expires, headers, options)
You have probably solved this by now but incase anyone else is stuck on this, as the lack of surrounding documentation does not make it very straight forward to implement.

Resources