What is the best practice to have a variable which lives for application lifetime in Node.js? - node.js

I am developing a web service 'A' using Node.js, which communicate with an another service 'B'. A needs to get an access token from B at the beginning, and it needs to refresh it when the token expires.
I am not very familiar with Node.js, I need to store access token and refresh token comes from B and refresh them time to time, but I am not sure if there are global variables to do this. For example, in Flask I would have kept my tokens in app['tokens'] and could be update it.
Basically what I want to do is to have a B class and a global object of B, B will have accessToken and refreshToken fields, obj.sendRequest({requestInfo}) will send a request to B service by checking expiration time of tokens and refresh them if necessary.
What is the best practice to overcome such a problem in Node.js?

From your question I don't see if you are using TypeScript or not. I highly recommend it, but it is not required. I will give you my answer with a TypeScript example because it is easier to read and understand (I think). Adapting it to pure JS should be easy.
class ApiService {
private accessToken?: string
private accessTokenExpires?: Date
private async getAccessToken(): Promise<void> {
const apiResult = await GET_TOKEN_FROM_API
this.accessToken = apiResult.access_token
this.accessTokenExpires = apiResult.access_token_expires
}
public async GetItem(id: string): Promise<Item> {
if (!this.accessToken || this.accessTokenExpires < new Date()) {
await this.getAccessToken()
}
return GET_ITEM_FROM_API
}
}
export let itemService = new ItemService()
export function mockitemService(mock: any) {
itemService = mock
}
at the place where you want to use the class you can
import { itemService } from './services/item'
const item = itemService.getItem('123')
Using ServiceClasses like this makes easier to maintain the code later or to write tests with mocked data sources.

Related

NestJS: Authorization based on instances property best practice

I need authorization in NestJS based on instances property.
Ex. user can update only his own articles.
Is there another way despite defining the logic in each services? ( I know it is possible using CASL )
Not having a global guard will facility errors, and everything is authorized by default unless add logic on the service.
What about creating a function that takes the request, the model and the name of the proprety and use it wherever you want ?
const verifAuthorization = (
req: Request,
propName: string,
model: any
): void => {
const sender: User = req.user;
if (!sender) {
throw new BadRequestException("there is no user in the token");
}
if (!sender._id.equals(model[propName])) {
throw new UnauthorizedException();
}
};
Yes ! you will call it in every service you want to check the authorization in, but it will save you a lot of time and code

createParamDecorator VS CanActivate in Nestjs for authorization

I'm trying to authorize users based on their permissions. Is there any difference in functionality between using createParamDecorator and CanActivate method?
export const GetUser = createParamDecorator((data: string, ctx: ExecutionContext) : User => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
const permissions = user.permissions
for(var i = 0; i < permissions.length; i++){
if(permissions[i].name === data)
return user;
}
throw new NotFoundException()})
These are absolutely not equivalent approaches, and should not be considered as such. The createParamDecorator is supposed to be an easy way to tell Nest how to inject a custom value (e.g. req.user instead of having to do #Req() { user }). It was never meant to be used for authorization, and while you can, it will most likely lead to very weird stack traces.
Guards, on the other hands, were made for authentication and authorization of the request. You can set metadata on a handler (e.g. what roles are allowed), read them with the Reflector, and then apply the conditional logic of if the request is valid or not. You're also able to use dependency injection on guards to add things like your database connection and get a full user from the database from an ID, which isn't possible in the createParamDecorator.
Lastly, in guards you can throw any error you want, or you can return false and get back a 403 by Nest's design.
Is there any difference in functionality...?
As mentioned, the dependency injection. It's also easier to test guards, in my opinion. But that's me.

Log Session User NodeJs NestJs

I am trying to log the session user but its not working as i suspected. I was thinking i could add it in the middle ware but clearly this wont work. Is there a strategy or pattern i should be using to accomplish this.
The PassportAuthInterceptor log should have distinct users but its only using the last one set by the Middleware
I see them using it in the log4js documentation but clearly this doesnt seem to make sense the way i demonstrated below.
I also see that the have a AuthLibrary.currentUser() call, but im unsure how to accomplish this.
https://github.com/log4js-node/log4js-node/blob/master/docs/layouts.md#tokens
seeing as how this is NestJS, can i inject the user into a service somehow as that could solve my problem.
export class LoggerMiddleware implements NestMiddleware {
private readonly logger = new AppLoggerService(LoggerMiddleware.name);
public use(req, res, next: () => void) {
log4js.getLogger('default').addContext('user', user.authName)
next();
}
}
So this is kinda gross but it works, i created 2 loggers one global (normal) and one session (scope request). The thing is that any class that is used by passport login cannot reference the SessionLoggerService .. This will have me split classes as global and Session so the classes they call can use the correct logger.
https://github.com/nestjs/nest/issues/1870
#Injectable({scope: Scope.REQUEST})
export class SessionLoggerService implements LoggerService {
public static logger = undefined;
constructor(#Inject(REQUEST) private readonly req) {
SessionLoggerService.logger = log4js.getLogger('user');
const user = this.req && this.req.user ? this.req.user : null;
if (user) {
SessionLoggerService.logger.addContext('user', user.authName);
} else {
SessionLoggerService.logger.addContext('user', '');
}
}
....
}

Transfer data from Zapier authentication to trigger

I am working on a Zapier app and there is a tenant id (integer) that is retrieved during authentication that I need to use in a trigger. What is the correct way to do this?
I have tried using global, bundle.authData and storing the data in a module, but nothing seems to work consistently. The best has been when I stored the data in global, but it is inconsistent, out of six calls to the trigger the tenant id may only be valid twice, the other four times it will be returned as undefined.
In the case of global I am writing the data during authentication:
const test = (z, bundle) => {
return z.request({
url: URL_PATH + ':' + URL_PORT + '/v1/auth',
params: {
username: bundle.authData.username,
password: bundle.authData.password
}
}).then((response) => {
if (response.status === 401) {
throw new Error('The username and/or password you supplied is incorrect.');
} else {
global.GLOBAL_tenant = response.json.tenant;
// ...
}
}
And then attempting to read the data back in the trigger:
const processTransactions = (z, bundle) => {
let jsonAll = [];
let tenant = global.GLOBAL_tenant;
return new Promise( (resolve, reject) => {
(function loop() {
// ...
I also tried adding the dat to 'bundle.authData', this was the recommendation that Zapier made when I contacted them, but the tenant id that I added during the authentication:
bundle.authData.tenant = response.json.tenant
Is not available when I try to retrieve it in the trigger. Only the 'username' and 'password' are present.
I am new to Zapier and node.js so any help will be greatly appreciated.
Instead of returning fully qualified name like bundle.authData.tenant = response.json.tenant, please use something like tenant = response.json.tenant and this statement should be enclosed in a return statement preferably. The bundle.authData qualifier is automatically applied by Zapier.
global variables should be avoided. Hope this helps.
David here, from the Zapier Platform team.
global isn't going to work because your code runs in multiple lambda executions and state isn't stored between them. Plus, global implies it would be the same for all users, which probably isn't what you want.
Instead, I'd check out session auth, which will let you store extra fields during your test by creating a computed field and returning values for it from sessionConfig.perform. Then it'll be stored in the auth object, next to the username and password.
Separately, you may want to consider whatever code is in processTransactions. Either you can return them all and they'll deduped on our end, or you're doing a bunch of extra computation that is better dehydrated. That's just a guess on my part though, so feel free to ignore this part.

owin oauth webapi with a dynamic TokenEndpointPath

I've successfully implemented oAuth using OWIN in my WebApi 2 Server with:
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions {
TokenEndpointPath = new PathString("/api/TokenByPassword"),
// ...
});
However, I would like the TokenEndpointPath to be dynamic as I will have multiple databases each with their own account records.
I believe I want something like:
TokenEndpointPath = new PathString("/api/{databaseid}/TokenByPassword");
I don't believe OAuthAuthorizationServerOptions supports this and even if it did - how would I get the databaseid ?
I could implement this in my own WebAPI with AttributeRouting, but then what would be the correct OWIN calls to make in that WebAPI to generate the correct BearerToken?
I found the answer..
Even though the TokenEndpointPath is specified in the OAuthAuthorizationServerOptions, the OAuthAuthorizationServerProvider has a delegate called OnMatchEndpoint. Inside this delegate, you can access the Request.Uri.AbsolutePath of the call and if it matches your criteria, you can then call MatchesTokenEndpoint() in which case OnGrantResourceOwnerCredentials will get called where you again can gain access the the Request.Uri and pick out the {databaseid} and use the correct database to Grant access.
OWIN is very flexible, but not immediately obvious which calls to make when to do what you want when it is something not quite straightforward.
Just to make it clearer, here is the implementation of the function MatchEndpoint of the class that extend OAuthAuthorizationServerProvider, as suggested by David Snipp :
private const string MatchTokenUrlPattern = #"^\/([\d\w]{5})\/token\/?$";
public override async Task MatchEndpoint(OAuthMatchEndpointContext context)
{
var url = context.Request.Uri.AbsolutePath;
if (!string.IsNullOrEmpty(url) && url.Contains("token"))
{
var regexMatch = new Regex(MatchTokenUrlPattern).Match(url);
if (regexMatch.Success)
{
context.MatchesTokenEndpoint();
return;
}
}
await base.MatchEndpoint(context);
}
Be careful on what you do in there because it is called at every request.

Resources