Is it alright to store user authentication token as a global variable (process.env) in a nodejs lambda function? - node.js

We have a BFF built with AWS Lambda (nodejs) and API Gateway that interfaces with an API that requires user authentication. And the way we've built it is we have a separate module/file for the API services. Something like this:
src
--handlers
--users.js // with function getMe()
--apiServices
--usersApi.js // with function getUser(id)
So what happens is the getMe() function will receive the event with the request headers with the authentication token. But we need to use the auth token in getUser(id). I've thought of two options to do this:
update getUser(id) to accept an authToken param.
store the auth token in the global variable
I'm preferring to do #2 because it requires less changes but I'm worried that this might not be a good idea because there's no way of knowing for sure when a lambda container will be reused (or if will be reused at all): https://aws.amazon.com/blogs/compute/container-reuse-in-lambda
Has someone tried the 2nd approach before? Or should I just go with #1? The thing with #1 is that we have a lot of files under apiServices with a lot of functions so I would like to apply as little change as possible.

You can do it both ways, but be careful and double check switching context between users because lambda persists for a short period of time and can be hit multiple times.

Related

Node.js express app architecture with testing

Creating new project with auto-testing feature.
It uses basic express.
The question is how to orginize the code in order to be able to test it properly. (with mocha)
Almost every controller needs to have access to the database in order to fetch some data to proceed. But while testing - reaching the actual database is unwanted.
There are two ways as I see:
Stubbing a function, which intends to read/write from/to database.
Building two separate controller builders, one of each will be used to reach it from the endpoints, another one from tests.
just like that:
let myController = new TargetController(AuthService, DatabaseService...);
myController.targetMethod()
let myTestController = new TargetController(FakeAuthService, FakeDatabaseService...);
myTestController.targetMethod() // This method will use fake services which doesnt have any remote connection functionality
Every property passed will be set to a private variable inside the constructor of the controller. And by aiming to this private variable we could not care about what type of call it is. Test or Production one.
Is that a good approach of should it be remade?
Alright, It's considered to be a good practice as it is actually a dependency injection pattern

How to handle provider-like objects in Actix-Web

I have a validate endpoint, which takes in a JWT that's POSTed to it, and that works fine. Currently set up like this in the application setup:
let server = HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.route("/ping", web::get().to(health_check))
.route("/validate", web::post().to(validate))
})
I'm now looking to provide some JWKs, which I've done via a provider-style setup, where the calling code can just call get_key and the provider should handle caching and refreshing that cache automatically every X minutes so that I don't have to call the endpoint that provides the JWKs on each request. However, obviously that will only work if I can maintain the same instance of the provider object.
What would be the best way of doing this? Could I create an instance of the provider at the same level as the server creation code and pass in the results of provider.get_key through the app_data method that actix provides? Or perhaps do the same via middleware somehow?
I've tried passing the entire provider instance through the app_data method but can't get this to work (I think because my struct can't implement Copy due to containing a Vec), so I'm trying to find alternate methods of doing it!

Passing OAuth token in multi threaded application

We have a SpringBoot application based on the Sap Cloud SDK (3.32.0) and are using PrincipalPropegation to our on-prem SAP environment.
Our application is also using the Axon Framework (an eventsourcing framework). This means our calls to our RestControllers are send as commands to the Aggregates, which in turn sends out events on the eventbus. Normally we pass the oauth token by adding metadata on the event messages. This is handled by the axon framework. Events are dispatched on different threads then the ones that process the commands.
However, we recently started using the cloud sdk and generated OData V2 clients to send/retrieve information to our on-prem SAP instances. The SAP cloud SDK tries to fetch the AuthToken from the ThreadContext, however, due to the async nature of the Axon framework, this does not work properly.
Is there a way pass the correct token in some other way and skip the default behaviour of the SDK? Since we have the token needed for doing the user token exchange for PrincipalPropegation in the event metadata (which can be accessed by the eventhandler).
Any suggestions would be great!
Danny
You can conveniently propagate the thread context to new threads using the ThreadContextExecutor:
ThreadContextExecutor executor = new ThreadContextExecutor();
Callable operationWithContext = () -> executor.execute(() -> operation());
invokeAsynchronously(operationWithContext);
Check out the documentation on the topic.
Is there a way pass the correct token in some other way and skip the default behaviour of the SDK?
In case the solution with ThreadContextExecutor is not working for you, we can look for a workaround: If you are looking for a way to pass an access token inside the child thread, then use the following code sample:
import com.sap.cloud.sdk.cloudplatform.security.AuthTokenAccessor;
import com.sap.cloud.sdk.cloudplatform.security.AuthToken;
DecodedJWT jwt = JWT.decode("your-access-token");
AuthToken authToken = new AuthToken(jwt);
AuthTokenAccessor.executeWithAuthToken(authToken, () -> {
// do things..
});
Please note: Besides current auth-token, the Cloud SDK may also extract principal and tenant information from the passed JWT.

Node typescript library environment specific configuration

I am new to node and typescript. I am working on developing a node library that reaches out to another rest API to get and post data. This library is consumed by a/any UI application to send and receive data from the API service. Now my question is, how do I maintain environment specific configuration within the library? Like for ex:
Consumer calls GET /user
user end point on the consumer side calls a method in the library to get data
But if the consumer is calling the user end point in test environment I want the library to hit the following API Url
for test http://api.test.userinformation.company.com/user
for beta http://api.beta.userinformation.company.com/user
As far as I understand the library is just a reference and is running within the consumer application. Library can for sure get the environment from the consumer, but I do not want the consumer having to specify the full URL that needs to be hit, since that would be the responsibility of the library to figure out.
Note: URL is not the only problem, I can solve that with environment switch within the library, I have some client secrets based on environments which I can neither store in the code nor checkin to source control.
Additional Information
(as per jfriend00's request in comments)
My library has a LibExecutionEngine class and one method in it, which is the entry point of the library:
export class LibExecutionEngine implements ExecutionEngine {
constructor(private environment: Environments, private trailLoader:
TrailLoader) {}
async GetUserInfo(
userId: string,
userGroupVersion: string
): Promise<UserInfo> {
return this.userLoader.loadUserInfo(userId, userGroupVersion)
}
}
export interface ExecutionEngine {
GetUserInfo(userId: string, userGroupVersion: string): Promise<UserInfo>
}
The consumer starts to use the library by creating an instance of the LibraryExecution then calling the getuserinfo for example. As you see the constructor for the class accepts an environment. Once I have the environment in the library, I need to somehow load the values for keys API Url, APIClientId and APIClientSecret from within the constructor. I know of two ways to do this:
Option 1
I could do something like this._configLoader.SetConfigVariables(environment) where configLoader.ts is a class that loads the specific configuration values from files({environment}.json), but this would mean I maintain the above mentioned URL variables and the respective clientid, clientsecret to be able to hit the URL in a json file, which I should not be checking in to source control.
Option 2
I could use dotenv npm package, and create one .env file where I define the three keys, and then the values are stored in the deployment configuration which works perfectly for an independently deployable application, but this is a library and doesn't run by itself in any environment.
Option 3
Accept a configuration object from the consumer, which means that the consumer of the library provides the URL, clientId, and clientSecret based on the environment for the library to access, but why should the responsibility of maintaining the necessary variables for library be put on the consumer?
Please suggest on how best to implement this.
So, I think I got some clarity. Lets call my Library L, and consuming app C1 and the API that the library makes a call out to get user info as A. All are internal applications in our org and have a OAuth setup to be able to communicate, our infosec team provides those clientids and secrets to individual applications, so I think my clarity here is: C1 would request their own clientid and clientsecret to hit A's URL, C1 would then pass in the three config values to the library, which the library uses to communicate with A. Same applies for some C2 in the future.
Which would mean that L somehow needs to accept a full configuration object with all required config values from its consumers C1, C2 etc.
Yes, that sounds like the proper approach. The library is just some code doing what it's told. It's the client in this case that had to fetch the clientid and clientsecret from the infosec team and maintain them and keep them safe and the client also has the URL that goes with them. So, the client passes all this into your library, ideally just once per instance and you then keep it in your instance data for the duration of that instance

Feathers JS nested Routing or creating alternate services

The project I'm working on uses the feathers JS framework server side. Many of the services have hooks (or middleware) that make other calls and attach data before sending back to the client. If I have a new feature that needs to query a database but for a only few specific things I'm thinking I don't want to use the already built out "find" method for this database query as that "find" method has many other unneeded hooks and calls to other databases to get data I do not need for this new query on my feature.
My two solutions so far:
I could use the standard "find" query and just write if statements in all hooks that check for a specific string parameter that can be passed in on client side so these hooks are deactivated on this specific call but that seems tedious especially if I find this need for several other different services that have already been built out.
I initialize a second service below my main service so if my main service is:
app.use('/comments', new JHService(options));
right underneath I write:
app.use('/comments/allParticipants', new JHService(options));
And then attach a whole new set of hooks for that service. Basically it's a whole new service with the only relation to the origin in that the first part of it's name is 'comments' Since I'm new to feathers I'm not sure if that is a performant or optimal solution.
Is there a better solution then those options? or is option 1 or option 2 the most correct way to solve my current issue?
You can always wrap the population hooks into a conditional hook:
const hooks = require('feathers-hooks-common');
app.service('myservice').after({
create: hooks.iff(hook => hook.params.populate !== false, populateEntries)
});
Now population will only run if params.populate is not false.

Resources