In a Blazor WebAssembly app I have one single server side method that returns results with circular references.
I found out that I can handle this situation on the server side by adding the following:
builder.Services.AddControllersWithViews()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});
and on the client side:
var options = new JsonSerializerOptions() { ReferenceHandler = ReferenceHandler.Preserve };
var r = await _http.GetFromJsonAsync<MyObject>>($"api/mycontroller/mymethod", options);
Unfortunately this way reference handling is enabled for every method on server. This introduces "$id" keys in almost all my methods results.
This would force me to change every client call to introduce ReferenceHandler.Preserve option.
Is there a way to specify ReferenceHandler.Preserve for some methods only (server side) or alternatively an option to force ReferenceHandler.Preserve for every GetFromJsonAsync (client side)?
You can use custom middleware in your sever . In custom middleware , you can put the method in it and do judge the URL passed by blazor. If the url meets the requirements, execute the method in the middleware, If not ,Just ignore this method.
Related
We are adding tenancy to our IdentityServer4 (https://github.com/IdentityServer/IdentityServer4) implementation. So far everything is good. AspIdentity appends a prefix to the username based on the hostname on the request ie. sso.domain1.com and sso.domain2.com create tenancy in the identity database. The external oauth for google works fine as well as google's API console allows multiple websites to access the same AppId. Facebook, on the other hand, only allows one domain per AppId. The external providers are registered during the application startup so this presents a problem as we need to determine the correct Facebook AppId to use per request based on the hostname.
Any suggestions on the appropriate way to handle this scenario? I tried having all Facebook AppIds registered at startup and letting the login page UI determine which Facebook button to make visible. IdentityServer threw an exception for this as it doesn't allow multiple providers with the same scheme name.
Is there somewhere in the pipeline we could overload to pass in the Request host and change the External Provider AppId per request?
Update 1:
Based on McGuireV10 answer I was able to get closer to the goal. The issue now is that in the event I can set the ClientId and ClientSecret option properties, but that doesn't change the Uri that was generated for the RequestUri property. Should I be doing it a different way or do I need to rebuild the context so it regenerates the RedirectUri? I've been trying to go through Microsoft's Security source code, but haven't been able to find this yet. Ideas?
services.AddAuthentication().AddFacebook(externalAuthentication.Name, options => {
options.SignInScheme = externalAuthentication.SignInScheme;
options.ClientId = externalAuthentication.DefaultClientId;
options.ClientSecret = externalAuthentication.DefaultClientSecret;
options.RemoteAuthenticationTimeout = TimeSpan.FromMinutes(5);
options.Events = new Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents {
OnRedirectToAuthorizationEndpoint = context => {
var tenancySetting = GetExternalAuthProviderForRequest(context.Request, externalAuthentication);
if (tenancySetting != null) {
context.Options.ClientId = tenancySetting.ClientId;
context.Options.ClientSecret = tenancySetting.ClientSecret;
}
context.RedirectUri = BuildChallengeUrl(context);
context.HttpContext.Response.Redirect(context.RedirectUri);
return Task.FromResult(0);
}
};
});
Update 2:
It is working now. I'm sure there must be a better way to do it, but I took the easy way out for now. I grabbed Microsoft's source code (https://github.com/aspnet/Security) and after looking through that I'm pretty sure that the HandleChallengeAsync method (Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler) is being called in the pipeline prior to entering the RedirectToAuthorizationEndpoint event. HandleChallegeAsync takes care of building the RedirectUri property on the context. There doesn't seem to be an existing method to rebuild the RedirectUri in Microsoft's code so I copied out their code for BuildChallegeUrl and used that to rebuild the RedirectUri. I updated the sample code to reflect this change.
Try adding an OpenIdConnectEvents handler for the OnRedirectToIdentityProvider event and replace the ClientId and ClientSecret properties there, but I don't know if that will confuse Identity Server in some way. I don't have a similar use-case so I haven't tried this specific thing myself, but I intercept other events and change the protocol properties without any problems. I also don't know if you'd still need to set the id and secret on the options property itself, but that's easy enough to test. It would look something like this:
services.AddAuthentication()
.AddFacebook("Facebook", options =>
{
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.ClientId = "abc";
context.ProtocolMessage.ClientSecret = "xyz";
return Task.FromResult<object>(null);
}
};
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
// you probably don't need these:
options.ClientId = oauth2config["FacebookId"];
options.ClientSecret = oauth2config["FacebookSecret"];
});
Obviously you'd need something more complex and implementation-specific to actually figure out the client etc.
I'm using Inversify.JS in a project with Express. I would like to create a connection to a Neo4J Database, and this process has two objets:
The driver object - Could be shared across the application and created one time only
The session object - Each HTTP request should create a session against the driver, whose lifecyle is the same as the http request lifecycle (as long as the request ends, the connection is destroyed)
Without Insersify.JS, this problem is solved using a simple algorithm:
exports.getSession = function (context) { // 'context' is the http request
if(context.neo4jSession) {
return context.neo4jSession;
}
else {
context.neo4jSession = driver.session();
return context.neo4jSession;
}
};
(example: https://github.com/neo4j-examples/neo4j-movies-template/blob/master/api/neo4j/dbUtils.js#L13-L21)
To create a static dependency for the driver, I can inject a constant:
container.bind<DbDriver>("DbDriver").toConstantValue(new Neo4JDbDriver());
How can I create a dependency instantiated only once per request and retrieve them from the container?
I suspect I must invoke the container on a middleware like this:
this._express.use((request, response, next) => {
// get the container and create an instance of the Neo4JSession for the request lifecycle
next();
});
Thanks in advance.
I see two solutions to your problem.
use inRequestScope() for DbDriver dependency. (available since 4.5.0 version). It will work if you use single composition root for one http request. In other words you call container.get() only once per http request.
create child container, attach it to response.locals._container and register DbDriver as singleton.
let appContainer = new Container()
appContainer.bind(SomeDependencySymbol).to(SomeDependencyImpl);
function injectContainerMiddleware(request, response, next) {
let requestContainer = appContainer.createChildContainer();
requestContainer.bind<DbDriver>("DbDriver").toConstantValue(new Neo4JDbDriver());
response.locals._container = requestContainer;
next();
}
express.use(injectContainerMiddleware); //insert injectContainerMiddleware before any other request handler functions
In this example you can retrieve DbDriver from response.locals._container in any request handler/middleware function registered after injectContainerMiddleware and you will get the same instance of DbDriver
This will work, but I'm not sure how performant it is. Additionally I guess that you may need to somehow dispose requestContainer (unbind all dependencies and remove reference to parent container) after http request is done.
I'm building an isomorphic React application which is using express.js on the server. The client app makes a number of AJAX requests to other express handler which currently entails them making multiple HTTP requests to itself.
As an optimisation I'd like to intercept requests I know the server handles and call them directly (thus avoiding the cost of leaving the application bounds). I've got as far as accessing the apps router to know which routes it handlers however I'm struggling to find the best way to start a new request. So my question is:
How do I get express to handle an HTTP request that comes from a programatic source rather than the network?
I would suggest create a common service and require it in both the handlers. What I do is break the business logic in the service and create controllers which handles the request and call specific services in this way u can use multiple services in same controller eg.
router.js
var clientController = require('../controllers/client-controller.js');
module.exports = function(router) {
router.get('/clients', clientController.getAll);
};
client-controller.js
var clientService = require('../services/client-service.js');
function getAll(req, res) {
clientService.getAll().then(function(data) {
res.json(data);
}, function(err) {
res.json(err);
});
}
module.exports.getAll = getAll;
client-service.js
function getAll() {
// implementation
}
module.exports.getAll = getAll;
u can also use something like http://visionmedia.github.io/superagent/ to make http calls from controllers and make use of them.
What is the best way to implement the following code in sails.js v0.10.5? Should I be handling this with a policy, and if so, how? The init() function required by Stormpath requires Express (app) as an argument. Currently, I am using the following code in sails.config.http.js as custom middleware.
customMiddleware: function(app) {
var stormpathMiddleware = require('express-stormpath').init(app, {
apiKeyFile: '',
application: '',
secretKey: ''
});
app.use(stormpathMiddleware);
}
Yes, this is the preferred way of enabling custom Express middleware with Sails if it does more than just handling a request (as in your case, where .init requires app). For simpler cases where you want to implement custom middleware that just handles requests, you can add the handler to sails.config.http.middleware and also add the handler name to the sails.config.http.middleware.order array. See the commented out defaults in config/http.js for an example using myRequestLogger.
Also note that the $custom key in the sails.config.http.middleware.order array indicates where the customMiddleware code will be executed, so you can change the order if necessary.
socket.io supports a single global authorization method with no middleware feature. Both express.io and passport.socketio depend on this feature as an injection point for their logic.
express.io attaches the express session to the request, and passport.socketio attaches the inflated user object. how do I combine the two features elegantly?
The only way I found is grabbing the authorization callback of express.io from socket.io and wiring it to be passport.socketio's success callback:
app.io.configure(function() {
var expressAuth = app.io.get('authorization');
var sessionConfig = {
...
success : expressAuth // callback on success
};
app.io.set('authorization', passportSocketIo.authorize(sessionConfig));
});
This works for me, but It's coupled with the order of the 'authorization' registrations. Any better ideas?