Why does OIDC login breaks in Edge but not in FireFox? - 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"]);
}
}
});

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.

Error Handling / Throw error in Strapi 4.0

in Strapi 4.0, i want to validate the input before saving. So i created lifecycles.js file as per the documentation and added the code:
module.exports = {
beforeCreate(event) {
//validation login here;
if (!valid) {
throw strapi.errors.badRequest('Invalid Entry');
}
},
}
How ever throw strapi.errors.badRequest('Invalid Entry'); is giving an error :
Cannot read property 'badRequest' of undefined
My guess is the Strapi v4 changed it from version 3. I looked everywhere but couldn't find a solution.
Any idea on how to handle error in lifecycles.js?
I had a similar situation with a forbidden error. I got to do it importing a class from #strapi/utils/lib/errors.js
const { ForbiddenError } = require("#strapi/utils").errors;
...
if (!authorized) {
throw new ForbiddenError(errorMessage);
}
You can show the list of errors based on your requirement
const { ValidationError } = require("#strapi/utils").errors;
...
if (formValidationError) {
throw new ForbiddenError("Fill the form");
}
Strapi comes with a lot of error response functions here are they
HttpError,
ApplicationError,
ValidationError,
YupValidationError,
PaginationError,
NotFoundError,
ForbiddenError,
PayloadTooLargeError,
UnauthorizedError,
PolicyError,

How to start GraphQL server when running .net5 integration tests?

I believe I am missing/misunderstanding something fundamental about the way .net5 works. In setting up an integration test environment for my GraphQL API, I am missing the step on how to start the GraphQL server from said test environment.
When I run the main project, the server is started properly and I can navigate to localhost in the browser and successfully execute GraphQL queries/mutations. My goal here is to set up some automated integration tests.
I'm using NUnit as my test runner and am using WebApplicationFactory<Startup> to "start the server" as I understand it.
In my test project, I'm under the impression that WebApplicationFactory<Startup> is supposed to basically use the Startup.cs class from my main project in my test project so that I don't have to duplicate all the settings, configurations, and injected services. Please correct me if that assumption is not correct.
I've pasted the code I think is relevant.
ApiWebApplicationFactory<Startup>
public class ApiWebApplicationFactory<TStartup> : WebApplicationFactory<Startup> where TStartup : class
{
public static IConfigurationRoot Configuration { get; set; }
public ApiWebApplicationFactory()
{
var configBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
Configuration = configBuilder.Build();
}
protected override void ConfigureClient(HttpClient client)
{
base.ConfigureClient(client);
client.BaseAddress = new Uri("https://localhost");
client.Timeout = new TimeSpan(0, 10, 0);
}
// Based on my assumption this class reuses everything in the Startup.cs class
// I don't actually think this is necessary, but thought it was worth trying
// the test with and without this code.
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddType<GraphQLContentItem>()
.AddType<GraphQLFolder>();
});
}
}
OneTimesetUp
[OneTimeSetUp]
public void OneTimeSetUp()
{
_factory = new ApiWebApplicationFactory<Startup>();
_client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.AddScoped<ICacheRepository, MockCache>();
});
}).CreateClient();
var connString = ApiWebApplicationFactory<Startup>.Configuration.GetConnectionString("DefaultConnection");
var options = new DbContextOptionsBuilder<CmsContext>()
.UseMySql(connString, ServerVersion.AutoDetect(connString))
.Options;
_dbContext = new CmsContext(options);
_dbContext.Database.EnsureCreated();
}
Test
[Test]
public async Task Test()
{
// If I set a breakpoint here, I can't navigate to the URL like I'm expecting to
var graphQLHttpClient =
new GraphQLHttpClient(
new GraphQLHttpClientOptions { EndPoint = new Uri("https://localhost/graphql") },
new NewtonsoftJsonSerializer(),
_client);
var request = new GraphQLRequest
{
Query = #"
query GetCurrentSession() {
getCurrentSession() {
id
name
}
}",
OperationName = "GetCurrentSession"
};
// Error is thrown here with "Bad Request"
var response = await graphQLHttpClient.SendQueryAsync<Session>(request);
// Further code is omitted
}
Please let me know if you see what I am missing. Thanks in advance~

How/where do I register the IDbPerTenantConnectionStringResolver

Trying to run core api host. i have this in the EntityFrameworkModule
public override void PreInitialize()
{
Configuration.MultiTenancy.IsEnabled = true;`
// CONNECTION STRING RESOLVER
Configuration.ReplaceService<IConnectionStringResolver, DbPerTenantConnectionStringResolver>(DependencyLifeStyle.Transient);
Configuration.DefaultNameOrConnectionString = MyConsts.DefaultConnectionStringName;
if (!SkipDbContextRegistration)
{
//DEFAULT
Configuration.Modules.AbpEfCore().AddDbContext<MyContext>(options =>
{
if (options.ExistingConnection != null)
MyContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
else
MyContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
});
}
}
ERROR
Mvc.ExceptionHandling.AbpExceptionFilter - Can't create component 'Ccre.EntityFrameworkCore.AbpZeroDbMigrator' as it has dependencies to be satisfied.
'Ccre.EntityFrameworkCore.AbpZeroDbMigrator' is waiting for the following dependencies:
- Service 'Abp.MultiTenancy.IDbPerTenantConnectionStringResolver' which was not registered.
How/where do I register the IDbPerTenantConnectionStringResolver?
I have this line in the PreInitialize of the Migrator.MigratorModule
Configuration.ReplaceService<IConnectionStringResolver, DbPerTenantConnectionStringResolver>(DependencyLifeStyle.Transient);
as well as in the EntityFrameworkModule

How can I open a facebook.session with access token and permissions in android?

How can I open a session with the specified permission?
I open a com.facebook.Session with API Session#openActiveSessionWithAccessToken() in an android.app.Service.
And then the opened session is used to get me/friendrequests by sending a request.
It results in an error as below:
{HttpStatus: 403, errorCode: 289, errorType: OAuthException, errorMessage: (#289) Requires extended permission: read_requests} ;
The permissions retrieved by using mSession.getPermissions() before creating the requests are as follows:
[read_requests, read_mailbox, manage_notifications]
Could you tell me what's wrong with my code?
Or could you tell me what should I do?
Code:
private static final List<String> PERMISSIONS = Arrays.asList("read_requests", "read_mailbox", "manage_notifications");
....
AccessToken token =
AccessToken.createFromExistingAccessToken(mAccount.getAccessToken(), new Date(mAccount.getExpireTime()), null,
AccessTokenSource.CLIENT_TOKEN, PERMISSIONS);
mSession = Session.openActiveSessionWithAccessToken(mContext, token, new StatusCallback(){
#Override
public void call(Session aSession, SessionState aState, Exception aException) {
if (aException != null) {
Log.i(Log.Tag.OTHER, TAG + "#createSession#call " + aException.getMessage());
}
//getNewInfos();
}
});
I have solved this problem.

Resources