Error between nginx and nestjs authentication part - javascript

I try to use a NestJS backend with a Nginx reverse proxy.
I have coded an authentication part in my NestJS backend.
My problem is that when I used my frontend / backend in local mode, all is ok.
When I use it through Nginx, I always retrieve a 401 error.
I think it’s due to the LocalStrategy in NestJS
Here is the part in the local.strategy.ts file
import { Strategy } from 'passport-local';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { AuthService } from '../auth.service';
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
usernameField: 'userLogin',
passwordField: 'userPassword',
});
}
async validate(userLogin: string, userPassword: string): Promise<any> {
const user = await this.authService.validateUser(userLogin, userPassword);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
Here is the part in the app.controller.ts file
#Public()
#UseGuards(LocalAuthGuard)
#Post('auth/login')
async login(#Request() req) {
return this.authService.login(req.user);
}
But I don’t know how to change it.
If somebody have an example it build be great.
Thanks in advance.

I've tested with curl locally on server (without nginx).
I saw my error (mysql access to test the user), it was not due to nginx configuration.

Related

Nestjs csrf is coming up as invalid

I am trying to test csrf protection in my nest application as per the nest documentation by using csurf but when I try to test using Postman its always showing invalid csrf.
I am using express platform which is default.
As per the documentation on csurf I am using express-session
I am applying csrf globally.
I try localhost:3000/csrf & get the token
then I go to localhost:3000/process in postman and add the token but get invalid csrf token
screenshot of how sending csrf in postman
Also tried sending _csrf as json { "_csrf": "----token_here-----" } but no go.
main.ts
import { ValidationPipe } from '#nestjs/common';
import { NestFactory } from '#nestjs/core';
import helmet from 'helmet';
import * as session from 'express-session';
import * as csrf from 'csurf';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use([
helmet(),
session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: {},
}),
csrf(),
]);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
}),
);
await app.listen(3000);
}
bootstrap();
app.controller.ts
import { Controller, Get, Post, Req } from '#nestjs/common';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(#Req() req): string {
return this.appService.getHello();
}
#Get('csrf')
getCsrf(#Req() request: any): string {
return request.csrfToken();
}
#Post('process')
postProcess() {
return "hello there, I am processing";
}
}

ConnectionNotFoundError: Connection "default" was not found. Can someone help me? [duplicate]

I use TypeORM with NestJS and I am not able to save properly an entity.
The connection creation works, postgres is running on 5432 port. Credentials are OK too.
However when I need to save a resource with entity.save() I got :
Connection "default" was not found.
Error
at new ConnectionNotFoundError (/.../ConnectionNotFoundError.ts:11:22)
I checked the source file of TypeORM ConnectionManager (https://github.com/typeorm/typeorm/blob/master/src/connection/ConnectionManager.ts) but it seems that the first time TypeORM creates connection it attributes "default" name if we don't provide one, which is the case for me.
I setup TypeORM with TypeOrmModule as
TypeOrmModule.forRoot({
type: config.db.type,
host: config.db.host,
port: config.db.port,
username: config.db.user,
password: config.db.password,
database: config.db.database,
entities: [
__dirname + '/../../dtos/entities/*.entity.js',
]
})
Of course my constants are correct. Any ideas ?
You are trying to create a repository or manager without the connection being established.
Try doing this const shopkeeperRepository = getRepository(Shopkeeper); inside a function. it will work
the upvoted answer is not necessarily correct, if you not specify the connection name it will default to "default".
const manager = getConnectionManager().get('your_orm_name');
const repository = manager.getRepository<AModel>(Model);
If anyone else has this problem in the future, check this out just in case:
I accidentally did "user.save()" instead of "userRepo.save(user)".
(And of course above initializing the connection like this:
const userRepo = getConnection(process.env.NODE_ENV).getRepository(User)
We are using lerna and using code from library A in package B.
The problem was that both TypeOrm versions in each package differ.
Solution is to make sure that you have exactly the same version installed in each package.
To be on the safe side, delete your node_modules directory and reinstall everything again with yarn install or npm install
Check your yarn.lock for multiple entries of typeorm and make sure there is only one.
If anyone using Express Router with getRepository(), check the code below
const router = Router();
router.get("/", async function (req: Request, res: Response) {
// here we will have logic to return all users
const userRepository = getRepository(User);
const users = await userRepository.find();
res.json(users);
});
router.get("/:id", async function (req: Request, res: Response) {
// here we will have logic to return user by id
const userRepository = getRepository(User);
const results = await userRepository.findOne(req.params.id);
return res.send(results);
});
Just make sure to call getRepository() in every route just like Saras Arya said in the accepted answer.
I follow the below approach creating the Database class. If the connection doesn't exist then it creates the connection else return the existing connection.
import { Connection, ConnectionManager, ConnectionOptions, createConnection, getConnectionManager } from 'typeorm';
export class Database {
private connectionManager: ConnectionManager;
constructor() {
this.connectionManager = getConnectionManager();
}
public async getConnection(name: string): Promise<Connection> {
const CONNECTION_NAME: string = name;
let connection: Connection;
const hasConnection = this.connectionManager.has(CONNECTION_NAME);
if (hasConnection) {
connection = this.connectionManager.get(CONNECTION_NAME);
if (!connection.isConnected) {
connection = await connection.connect();
}
} else {
const connectionOptions: ConnectionOptions = {
name: 'default',
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'DemoDb',
synchronize: false,
logging: true,
entities: ['src/entities/**/*.js'],
migrations: ['src/migration/**/*.js'],
subscribers: ['src/subscriber/**/*.js'],
};
connection = await createConnection(connectionOptions);
}
return connection;
}
}
If you are using webpack the make sure entities are imported specifically & returned in array.
import {User} from 'src/entities/User.ts';
import {Album} from 'src/entities/Album.ts';
import {Photos} from 'src/entities/Photos.ts';
const connectionOptions: ConnectionOptions = {
name: 'default',
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'DemoDb',
synchronize: false,
logging: true,
entities: [User, Album, Photos],
migrations: ['src/migration/**/*.js'],
subscribers: ['src/subscriber/**/*.js'],
};
Finally
const connectionName = 'default';
const database = new Database();
const dbConn: Connection = await database.getConnection(connectionName);
const MspRepository = dbConn.getRepository(Msp);
await MspRepository.delete(mspId);
For those of you looking for another answer, check this out.
In my case, the issue was because I was passing name in my db config.
export const dbConfig = {
name: 'myDB',
...
}
await createConnection(dbConfig) // like this
As a result, the only connection server knows is myDB not default.
At the same time, in my service, repository was injected without name which will fallback to default. (Service will looking for default connection as a result)
#Service() // typedi
export class Service {
constructor(
// inject without name -> fallback to default
#InjectRepository() private readonly repository
) {}
}
As a fix, I removed name property in my db config.
Or you can pass myDB as a parameter for InjectRepository like #InjectRepository('myDB'), either way works.
In my own case, the actual problem was that my index file imports my router file which imports my controllers which then import my services (where the call to getRepository was made). So the imports were resolving (and thus the call to getRepository) before the connection was established.
I considered implementing Sarya's answer but it's going to leave my code more verbose.
What I did was create a function to connect to the DB in a db/index.ts file
import { createConnection } from "typeorm";
export const getDBConnection = async () => {
const dbConnection = await createConnection();
if (!dbConnection.isConnected) await dbConnection.connect();
return dbConnection;
}
Then create an async function to bootstrap my app. I wait on getDBConnection to resolve before instantiating my app then I import my router file after. That way the import resolution only happens after the connection has been established.
routers/index.ts
import { Router } from 'express';
const router = Router();
/* ... route configurations ... */
export default router;
app.ts
const bootstrap = async () => {
try {
// wait on connection to be established
await getDBConnection();
} catch (error) {
// log error then throw
throw error;
}
// create app
const app = express();
// some middleware configuration...
// now import and setup the router
const { default: router } = await import("./routers");
app.use("/api", router);
// some more middleware configuration...
const server = http.createServer(app);
server.listen(3000, () => console.log('app running at port: 3000'));
};
bootstrap();
I got this error while using getConnectionOptions for different environments. Using one database for development and another for testing. This is how I fixed it:
const connectionOptions = await getConnectionOptions(process.env.NODE_ENV);
await createConnection({...connectionOptions, name:"default"});
I usegetConnectionOptions to get the connection for the current environment, in order to do that successfully you have to change ormconfig.json to an array, with keys "name" containing the different environments you want, like so:
[
{
"name" : "development",
"type": "USER",
"host": "localhost",
"port": 5432,
"username": "postgres",
"password": "PASS",
"database": "YOURDB"
},
{
"name" : "test",
"type": "USERTEST",
"host": "localhost",
"port": 5432,
"username": "postgres",
"password": "PASSTEST",
"database": "YOURDBTEST"
}
]
Now connectionOptions will contain the connection parameters of the current environment, but loading it to createConnection threw the error you pointed. Changing connectionOptions name to "default" fixed the issue.
I know it is super weird but someone might need this:
Windows related reason.
I had the same error caused by the current location set with the drive letter in the lower case (d:/apps/app-name/etc).
The problem got fixed once I updated the directory change instruction to use capital D (D:/apps/app-name/etc).
After verifying TypeOrm versions is same in both the packages i.e- external package and consumer repository as mentioned by #InsOp still issue persist then issue could be-
Basically when we create an external package - TypeORM tries to get the "default" connection option, but If not found then throws an error:
ConnectionNotFoundError: Connection "default" was not found.
We can solve this issue by doing some kind of sanity check before establishing a connection - luckily we have .has() method on getConnectionManager().
import { Connection, getConnectionManager, getConnectionOptions,
createConnection, getConnection, QueryRunner } from 'typeorm';
async init() {
let connection: Connection;
let queryRunner: QueryRunner;
if (!getConnectionManager().has('default')) {
const connectionOptions = await getConnectionOptions();
connection = await createConnection(connectionOptions);
} else {
connection = getConnection();
}
queryRunner = connection.createQueryRunner();
}
Above is a quick code-snippet which was the actual root cause for this issue but If you are interested to see complete working repositories (different examples) -
External NPM Package :
Git Repo : git-unit-of-work (specific file- src/providers/typeorm/typeorm-uow.ts)
Published in NPM : npm-unit-of-work
Consumer of above package : nest-typeorm-postgre (specific files- package.json, src/countries/countries.service.ts & countries.module.ts)
In my case was that I have an array of multiple connections, instead of just one. You have 2 alternatives.
To have at least one default named connection, example:
createConnections([
{
name: 'default',
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'users',
entities: [`${__dirname}/entity/*{.js,.ts}`],
synchronize: true,
logging: true
}
]);
To be specific when using the connection:
import {getConnection} from "typeorm";
const db1Connection = getConnection("db1Connection");
// you can work with "db1" database now...
I had this same problem with the following code:
import { HttpException, Inject, NotFoundException } from "#nestjs/common";
import { Not } from "typeorm";
import { Transactional } from "typeorm-transactional-cls-hooked";
import { TENANT_CONNECTION } from "../tenant/tenant.module";
import {Feriados} from './feriados.entity';
export class FeriadosService {
repository: any;
constructor(
#Inject(TENANT_CONNECTION) private connection)
{
this.repository = connection.getRepository(Feriados)
}
#Transactional()
async agregar(tablaNueva: Feriados): Promise<Number> {
const tablaAGuardar = await this.repository.create(tablaNueva)
return await this.guardar(tablaAGuardar)
}
#Transactional()
async actualizar(tablaActualizada: Feriados): Promise<Number>{
const tablaAGuardar = await this.repository.merge(tablaActualizada);
return await this.guardar(tablaAGuardar)
}
async guardar(tabla:Feriados){
await this.repository.save(tabla)
return tabla.id
}
I fixed it by removing the 2 #Transactional()
I hope someone helps.
In typeorm v0.3 the Connection API was replaced by the DataSource API. NestJS adapted this change as well, so if you relied on the old API (e.g. getConnection method) you might see the Connection "default" was not found error.
You can read about the changes and the new API in the release notes: https://github.com/typeorm/typeorm/releases/tag/0.3.0
If you used getConnection you can use app.get(DataSource) instead.
In the new version of Typeorm, 0.3.7, a solution to this problem is next:
In the app.module.ts, change the constructor of the AppModule class and create a method to return Datasource:
export class AppModule {
constructor(private dataSource: DataSource) {}
getDataSource() {
return this.dataSource;
}
}
Then, in the file you need to use add:
const repository = app
.get(AppModule)
.getDataSource()
.getRepository('Entity_name');
Although Saras Arya has provided the correct answer, I have encountered the same error
ConnectionNotFoundError: Connection "default" was not found.
due to the fact that my typeORM entity did have an #Entity() decorator as well as that it had extended BaseEntity.
The two can't live together.

Next.js middleware Module not found: Can't resolve 'fs'

Getting this error in Next.js _middleware file when I try to initialize Firebase admin V9. Anyone know how to solve this issue?
./node_modules/#google-cloud/storage/build/src/bucket.js:22:0
Module not found: Can't resolve 'fs'
../../firebase/auth-admin
import * as admin from "firebase-admin";
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert({
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_ADMIN_PRIVATE_KEY,
}),
});
}
const firestore = admin.firestore();
const auth = admin.auth();
export { firestore, auth };
Calling it in my _middleware
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
import { auth } from "../../firebase/auth-admin";
export default async function authenticate(
req: NextRequest,
ev: NextFetchEvent
) {
const token = req.headers.get("token");
console.log("auth = ", auth);
// const decodeToken = await auth.verifyIdToken(token);
return NextResponse.next();
}
I saw a solution here by customizing webpack but this does not fix it.
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
webpack: (config, { isServer, node }) => {
node = {
...node,
fs: "empty",
child_process: "empty",
net: "empty",
tls: "empty",
};
return config;
},
};
module.exports = nextConfig;
The Edge Runtime, which is used by Next.js Middleware, does not support Node.js native APIs.
From the Edge Runtime documentation:
The Edge Runtime has some restrictions including:
Native Node.js APIs are not supported. For example, you can't read or write to the filesystem
Node Modules can be used, as long as they implement ES Modules and do not use any native Node.js APIs
You can't use Node.js libraries that use fs in Next.js Middleware. Try using a client-side library instead.
I wasted a lot of time tying to get this to work. The weird thing is that this will work in the api itself.
So instead of calling firebase-admin action in the _middleware file. Call it in the api itself like:
import type { NextApiRequest, NextApiResponse } from 'next'
import { auth } from "../../firebase/auth-admin";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const authorization = req.headers.authorization
console.log(`Handler auth header: ${authorization}`)
if (!authorization) {
return res.status(401).json({ message: 'Authorisation header not found.' })
}
const token = authorization.split(' ')[1]
if (!token) {
return res.status(401).json({ message: 'Bearer token not found.' })
}
console.log(`Token: ${token}`)
try {
const {uid} = await auth.verifyIdToken("sd" + token)
console.log(`User uid: ${uid}`)
res.status(200).json({ userId: uid })
} catch (error) {
console.log(`verifyIdToken error: ${error}`)
res.status(401).json({ message: `Error while verifying token. Error: ${error}` })
}
}
A workaround to make this reusable is to create a wrapper function.
If anyone knows how to make this work in a _middleware file, I would be really grateful.
Edit: Gist for the wrapper middleware function:
https://gist.github.com/jvgrootveld/ed1863f0beddc1cc2bf2d3593dedb6da
make sure you're not calling firebase-admin in the client
import * as admin from "firebase-admin";
I've recently released a library that aims to solve the problem: https://github.com/ensite-in/next-firebase-auth-edge
It allows to create and verify tokens inside Next.js middleware and Next.js 13 server components. Built entirely upon Web Crypto API.
Please note it does rely on Next.js ^13.0.5 experimental "appDir" and "allowMiddlewareResponseBody" features.

Trouble Deploying Angular/Express App in Heroku frontend cannot hit the API endpoint

Currently in development it works just fine... localhost:4200 for the front-end and localhost:8080 for the back-end
However, I just deployed it and the front-end get displayed, but isn't getting the data from the API because in my app.service.ts I'm doing the following:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class ApiService {
private apiUrl = 'http://localhost:8080/api'
constructor(private http: HttpClient) { }
public getNews() {
return this.http.get(`${this.apiUrl}/countries`)
}
}
As you can see, I'm hardcoding the localhost:8080 and it works fine in development, but when it comes to production Heroku does not assign me the port 8080, it assigns me another one.
That being said... How can I tweak this in order to read the port Heroku gives me?
This is my app.js file
const express = require('express');
const app = express();
const scrapper = require('./backend/scrapper')
// Create link to Angular build directory
var distDir = __dirname + "/dist/covid19";
app.use(express.static(distDir));
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PATCH, PUT, DELETE, OPTIONS"
);
next();
});
app.use("/api/countries", async (req, res, next) => {
const data = await scrapper.getCountries()
res.status(200).json(data)
})
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log(`API listening on port ${port}...`);
});
module.exports = app;
As you can see I'm declaring my port to be process.env.PORT || 8080, but this is for the backend... How can achieve this but in my API call in the service.ts file?
You guys pointed me in the right direction, but to be precise:
I noticed that in Angular you get a environments folder with two files 1. environment.ts and environment.prod.ts.
I just had to make sure to use to point to the URL that Heroku gave me for my app after deploying yourappname.herokuapp.com, by doing the following in my environments.prod.ts (Which is the one that Heroku is gonna look for)
export const environment = {
production: true,
apiUrl: "https://yourappname.herokuapp.com/api"
};
And in my api.service.ts I ended up with the following code:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { environment } from '../environments/environment'
const API_URL = environment.apiUrl;
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
public getNews() {
return this.http.get(API_URL + '/countries')
}
}
When you deploy a web server on Heroku you bind to the $PORT Heroku tells you to bind to.
When you visit your deployed app you don't specify a port. You just connect to yourappname.heroku.com. The DNS automatically translates it into ipaddress:port.
So on your frontend you just point to yourappname.heroku.com instead of ipaddress:port.

"fake path" issue using multer+angular 6

I spend the last 3 days to fix the problem , but i didnt figure out yet the issue.
Angular CLI: 6.0.8
Node: 8.11.2
OS: win32 x64
Angular: 6.0.6
multer. 1.3.1
my code at "childApi" using multer staff :
var store = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads');
},
filename: function (req, file, cb) {
cb(null, Date.now() + '.' + file.originalname);
}
});
var upload = multer({ storage: store , }).single('file');
router.post('/upload', function (req, res, next) {
upload(req, res, function (err) {
if (err) {
return console.log ('not working well')
}
//do all database record saving activity
return res.json({ originalname: req.file.originalname, uploadname: req.file.filename });
});
});
my code at "add-child" component using simple code :
import { Component, OnInit, Inject } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '#angular/material';
import { Child } from '../../models/child';
import { ChildService } from '../../services/child.service';
import {FileUploader } from 'ng2-file-upload';
const uri = 'http://localhost:3000/childApi/upload';
#Component({
selector: 'app-add-child',
templateUrl: './add-child.component.html',
styleUrls: ['./add-child.component.css']
})
export class AddChildComponent implements OnInit {
newChild = new Child;
uploader: FileUploader = new FileUploader({ url: uri });
attachmentList: any = [];
constructor(private childService: ChildService,
private route: ActivatedRoute,
private router: Router,
public dialogRef: MatDialogRef<AddChildComponent>,
#Inject(MAT_DIALOG_DATA) public data: any) {
this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
this.attachmentList.push(JSON.parse(response));
};
}
The problem is that after I upload the file to the folder "uploads"
,I want to display my new photo on the screen.
The console give me this error :
GET unsafe:C:\fakepath\child+thinking.jpg 0 ()
If someone help its will be amazing.
Thanks...
I figure out what to do , I just put this sentences inside my code at "add-child" component using :
this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
this.newChild.user_img = JSON.parse(response).uploadname;
this.attachmentList.push(JSON.parse(response));
};
}
As I understand from your post that you have doing a model named child inside your project so if you have can I take a look on it I will be grateful because I'm doing the same task except still getting the error:
Access to XMLHttpRequest at 'http://localhost:4000/file/upload' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
core.js:1449 ERROR SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at FileUploader.UploadFileComponent.uploader.onCompleteItem (upload-file.component.ts:27)
at FileUploader.push../node_modules/ng2-file-upload/file-upload/file-uploader.class.js.FileUploader._onCompleteItem (file-uploader.class.js:199)
at XMLHttpRequest.xhr.onerror [as __zone_symbol__ON_PROPERTYerror] (file-uploader.class.js:268)`
javascript html typescript angular6 multer

Categories