Next Auth - client_id is required, what am I missing? - javascript

I am slightly confused as to where the disconnect is happening.
[...nextauth].js
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET
})
],
secret:process.env.JWT_SECRET
})
.env
GOOGLE_CLIENT_ID = qwertclientid
GOOGLE_CLIENT_SECRET = qwertyclientsecret
NEXTAUTH_URL=http://localhost:3000
JWT_SECRET=qwertysecret
I am getting the error:
error: {
message: 'client_id is required',
providerId: 'google',
message: 'client_id is required'
}
If I don't use process.env.GOOGLE_CLIENT_ID from the .env file and I use a string, it works as intended. If i try and use it from the .env file I get the above error. I have also tried to use a string and non string in the .env file.

Related

NestJS: Pino Logger not working correctly with GraphQL requests

The bounty expires in 4 days. Answers to this question are eligible for a +50 reputation bounty.
mh377 wants to draw more attention to this question.
I am using NestJS v9 with the express adapter and the #nestjs/graphql library and am having issues extracting headers from the graphql request and appending them to the log messages using the pino logging library.
Below is my LoggerModule
import { Module } from '#nestjs/common';
import { ConfigModule, ConfigService } from '#nestjs/config';
import { LoggerModule as PinoLoggerModule } from 'nestjs-pino';
#Module({
imports: [
PinoLoggerModule.forRootAsync({
imports: [ConfigModule.forRoot({ isGlobal: true })],
useFactory: async (configService: ConfigService) => ({
isGlobal: true,
pinoHttp: {
level: process.env.LOG_LEVEL || 'info',
redact: configService.get<string[]>('logger.redact.fields'),
transport: {
target: 'pino-pretty',
options: {
colorize: false,
singleLine: true,
levelFirst: false,
translateTime: "yyyy-mm-dd'T'HH:MM:ss.l'Z'",
messageFormat: '{req.headers.x-correlation-id} [{context}] {msg}',
ignore: 'pid,hostname,context,req,res.headers',
errorLikeObjectKeys: ['err', 'error'],
},
},
},
}),
inject: [ConfigService],
}),
],
controllers: [],
providers: [],
})
export class LoggerModule {}
As you can see the message format is configured to be:
messageFormat: '{req.headers.x-correlation-id} [{context}] {msg}',
However, the log message doesn't display the req.headers.x-correlation-id only the context and the msg.
I have tested the configuration with REST requests and the id appears in the logs as expected so it is an issue with GraphQL requests only.
Does anybody know how I can fix this issue ?
Here is a link to a sample github repo with my code
https://github.com/mh377/nestjs-graphql-sample
I have raised an issue with the following libraries
nestjs-pino
https://github.com/iamolegga/nestjs-pino/issues/1342
pino-http
https://github.com/pinojs/pino-http/issues/273
pino-pretty
https://github.com/pinojs/pino-pretty/issues/402

I want to use my environment.ts file URL variable in my common services, how can I read that variable?

My environment file
export environment = {
local: true,
title: 'Local Environment Heading',
API URL : 'http://localhost:8090/myapp/',
staging: false,
production: false,
};
Common service file
// here I want to read this API URL
In this case, you have an error on your enviroment file.
Check the name API URL it couldn't contain space on the name.
And you forgot the const on the export. (export const enviroment )
export const environment = {
local: true,
title: 'Local Environment Heading',
baseUrl : 'http://localhost:8090/myapp/',
staging: false,
production: false,
};
After fixing that, it's very simple to use. You just have to import the enviroment file.
For example in a service.
import { Injectable } from '#angular/core';
import { environment } from 'src/environments/environment';
#Injectable({
providedIn: 'root'
})
export class AuthServiceService {
constructor() {
const a = environment.baseUrl
}
}

Cannot find name 'ENV' returnTo: ENV.BASE_URI

I assume my authentication service fails to load a configuration file causing an error.
Im following along a tutorial on AUTH0.Here is the link https://auth0.com/blog/real-world-angular-series-part-2/.
I have to import a file from env.config.ts that will be used by my authentication service.Here is the code env.config.ts
const _isDev = window.location.port.indexOf('4200') > -1;
const getHost = () => {
const protocol = window.location.protocol;
const host = window.location.host;
return `${protocol}//${host}`;
};
const apiURI = _isDev ? 'http://localhost:8083/api/' : `/api/`;
export const ENV = {
BASE_URI: getHost(),
BASE_API: apiURI
};
Here is a quote from the tutorial "This code detects the host environment and sets the app's base URI and base API URI. We'll import this ENV configuration wherever we need to detect and use these URIs."
This env file is imported into a file required by auth0,
Here is my snippet of the auth.config.ts
import { ENV } from './../core/env.config';
interface AuthConfig {
CLIENT_ID: string;
CLIENT_DOMAIN: string;
AUDIENCE: string;
REDIRECT: string;
SCOPE: string;
NAMESPACE: string;
};
export const AUTH_CONFIG: AuthConfig = {
CLIENT_ID: '[xxx]',
CLIENT_DOMAIN: '[]', // e.g., kmaida.auth0.com
AUDIENCE: '[http://localhost:8083/api/]', // e.g., http://localhost:8083/api/
REDIRECT: `${ENV.BASE_URI}/callback`,
SCOPE: 'openid profile email',
NAMESPACE: 'http://myapp.com/roles'
};
Please have a look at the tutorial and maybe share what i could have missed.
The auth.service.ts uses the configutaion file as such
// Remove data from localStorage
this._clearExpiration();
this._clearRedirect();
// End Auth0 authentication session
this._auth0.logout({
clientId: AUTH_CONFIG.CLIENT_ID,
returnTo: ENV.BASE_URI
});
}
this is what my imports section looks like of auth.service.ts
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { BehaviorSubject, Subscription, of, timer } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { AUTH_CONFIG } from './auth.config';
import * as auth0 from 'auth0-js';
import { ENV } from './../core/env.config';
The error emmited in my cmd reads
ERROR in src/app/auth/auth.service.ts:92:17 - error TS2304: Cannot find name >'ENV'.
92 returnTo: ENV.BASE_URI.
I also suspect the way the imports are handled from core have a problem ,as i really im not well vast with the slash imports.
I have my app directory that holds the auth and core folder as direct children.
try declaring ENV inside a constructor.
public constructor(){
const env = ENV
}
Then use it.
console.log(env.BASE_URI)

celebrate npm package with Nest Framework

Is there some way to use celebrate NPM package with Nest Framework? In the documentation they only refer to class-validator, but although this has many advantages I have used with Express and other frameworks the celebrate middleware to validate the request. In the case of Nest, the middleware configuration is done in the app.module.ts file, but in others as routing-controllers the decorator #UseBefore is used for the middleware in the controllers, that is why I would appreciate any explanation, example or documentation how to use this middleware with Nest. Thanks!!
The solution:
In a file dedicated to the validations create the middleware with the scheme to use by celebrate (this would be a simple example for an authentication):
import { celebrate, Joi } from 'celebrate';
export const authenticate = celebrate({
headers: Joi.object().keys({
client: Joi.string().required(),
application: Joi.string().required(),
}).unknown(),
body: Joi.object().keys({
email: Joi.string().email().required(),
password: Joi.string().required(),
}),
});
How to use it later? Import it in the module and apply it as a middleware, since celebrate returns a function of type function (req, res, next):
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '#nestjs/common';
// Others imports ...
import { authenticate } from './validations/authenticate.validation';
#Module({
imports: [ // Your imports... ],
controllers: [ // Your controllers... ],
providers: [ // Your providers... ],
})
export class AuthenticationModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// Validate Login.
consumer.apply(authenticate).forRoutes({ path: 'security/login', method: RequestMethod.POST });
}
}
The path defined in the configuration must obviously match the endpoint of the controller:
import {
Body,
Post,
Headers,
Controller,
} from '#nestjs/common';
#Controller('security')
export class AuthenticationController {
#Post('login')
async authenticate(
#Body('email') email: string,
#Body('password') password: string,
#Headers('application') application: string): Promise<any> {
// Autenticate user
}
}
References:
https://docs.nestjs.com/middleware
https://www.npmjs.com/package/celebrate

NestJS + TypeORM: Use two or more databases?

I'm trying since 2 days to solve this, perhaps I'm simply missing the point here.
My goal was to write a NestJS app (with TypeORM included) which serves a RestAPI for 2 or 3 of my little projects, instead of writing a NestJS-App for every single one of them.
So far so good, the app is ready, works well with the single projects (which resides in subfolders with their entities, controllers, services, modules), but I can't get it to run with all of them.
The point seems to be the configuration, I'm using ormconfig.json:
[ {
"name": "Project1",
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "<username>",
"password": "<pwd>",
"database": "<database>",
"synchronize": false,
"entities": ["project1/*.entity.ts"],
"subscribers": ["project1/*.subscriber.ts"],
"migrations": ["project1/migrations/*.ts"],
"cli": { "migrationsDir": "project1/migrations" }
}, {
"name": "project2",
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "<another-username>",
"password": "<another-pwd>",
"database": "<another-database>",
"synchronize": false,
"entities": ["project2/*.entity.ts"],
"subscribers": ["project2/*.subscriber.ts"],
"migrations": ["project2/migrations/*.ts"],
"cli": { "migrationsDir": "project2/migrations"
} ]
The error message says:
[ExceptionHandler] Cannot find connection default because its not defined in any orm configuration files
Of course "default" couldn't be found, because I'm providing two configs with unique names different to "default".
In ApplicationModule I could provide the name of the connection, like this:
TypeOrmModule.forRoot( { name: "project1" } ),
but then it would work only for one project.
I could mix all in one config, but then I would have everything in one database, same user for all and perhaps mix up the entities...
Can someone give me a hint how to solve this?
Perhaps with getConnection(<name>) in every module, but how to start the ApplicationModule then?
Kind regards,
sagerobert
I just tried setting up TypeORM with multiple databases and a ormconfig.json and it did not work for me at all. It seemed to always use the default connection and when no default (= without explicit name) connection was found it threw the corresponding error.
It did work though when I defined the connections in the app.module.ts instead (I removed ormconfig.json):
imports: [
...,
TypeOrmModule.forRoot({
name: 'Project1',
type: 'mysql',
host: 'localhost',
port: 3306,
username: '<username>',
password: '<pwd>',
database: '<database>',
synchronize: false,
entities: ['project1/*.entity.ts'],
subscribers: ['project1/*.subscriber.ts'],
migrations: ['project1/migrations/*.ts'],
cli: { migrationsDir: 'project1/migrations' },
}),
TypeOrmModule.forRoot({
name: 'project2',
type: 'mysql',
host: 'localhost',
port: 3306,
username: '<another-username>',
password: '<another-pwd>',
database: '<another-database>',
synchronize: false,
entities: ['project2/*.entity.ts'],
subscribers: ['project2/*.subscriber.ts'],
migrations: ['project2/migrations/*.ts'],
cli: { migrationsDir: 'project2/migrations' },
})
]
You need to explicitly pass the connection name at the same level inside TypeOrmModule.forRoot({ name: 'db1Connection' }) incase you are using multiple database connections.
TypeOrmModule.forRootAsync({
name: DB1_CONNECTION,
imports: [ConfigModule],
useClass: TypeormDb1ConfigService,
}),
TypeOrmModule.forRootAsync({
name: DB2_CONNECTION,
imports: [ConfigModule],
useClass: TypeormDb2ConfigService,
})
For clarity and for other developers to come to this post:
From NestJS documentation:
If you don't set any name for a connection, its name is set to default. Please note that you shouldn't have multiple connections without a name, or with the same name, otherwise they simply get overridden.
One of your connections must have one of the following:
"name":"default"
Without any name.
I would recommend to declare all your connections in ormconfig.json and not declare it in code.
An example to import the connections from ormconfig.json:
#Module({
imports: [TypeOrmModule.forFeature([Entity1, Entity2]), //This will use default connection
TypeOrmModule.forRoot({name: 'con1'}), // This will register globaly con1
TypeOrmModule.forRoot({name: 'con2'}), // This will register globaly con2
controllers: [...],
providers: [...],
exports: [...]
})
in your module (not have to be the root module, only the modules you will need the connections).
for those who face this problem , this is my solution
AppModule
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [
database,
databaseAllo #<= my second database
]
}),
TypeOrmModule.forRootAsync({
useFactory: (configs: ConfigService) => configs.get("db_config"),
inject: [ConfigService],
}),
TypeOrmModule.forRootAsync({
name:"db_allo", #<= create connection to my second db
useFactory: (configs: ConfigService) => configs.get("db_config_allo"),
inject: [ConfigService],
}),
AuthModule,
JwtAuthModule
],
controllers: []
})
export class AppModule {}
my project module ( contain table from second db )
#Module({
imports: [
TypeOrmModule.forFeature([AlloMpcTable], "db_allo" #<= call connection again),
],
providers: [
AlloRepository
],
exports: [AlloRepository],
controllers: [],
})
export class AlloModule {}
my project repository
#Injectable()
export class AlloRepository extends BaseRepository<AlloMpcTable> {
constructor(
#InjectRepository(AlloMpcTable, "db_allo") #<= you need to call connection again
private readonly allo: Repository<AlloMpcTable>,
) {
super(allo)
}
public async Find(id: number): Promise<AlloMpcTable> {
return await this.allo.findOne(id)
}
}
This is how I've manage to fix it. With a single configuration file I can run the migrations on application boostrap or using TypeOrm's CLI.
src/config/ormconfig.ts
import parseBoolean from '#eturino/ts-parse-boolean';
import { TypeOrmModuleOptions } from '#nestjs/typeorm';
import * as dotenv from 'dotenv';
import { join } from 'path';
dotenv.config();
export = [
{
//name: 'default',
type: 'mssql',
host: process.env.DEFAULT_DB_HOST,
username: process.env.DEFAULT_DB_USERNAME,
password: process.env.DEFAULT_DB_PASSWORD,
database: process.env.DEFAULT_DB_NAME,
options: {
instanceName: process.env.DEFAULT_DB_INSTANCE,
enableArithAbort: false,
},
logging: parseBoolean(process.env.DEFAULT_DB_LOGGING),
dropSchema: false,
synchronize: false,
migrationsRun: parseBoolean(process.env.DEFAULT_DB_RUN_MIGRATIONS),
migrations: [join(__dirname, '..', 'model/migration/*.{ts,js}')],
cli: {
migrationsDir: 'src/model/migration',
},
entities: [
join(__dirname, '..', 'model/entity/default/**/*.entity.{ts,js}'),
],
} as TypeOrmModuleOptions,
{
name: 'other',
type: 'mssql',
host: process.env.OTHER_DB_HOST,
username: process.env.OTHER_DB_USERNAME,
password: process.env.OTHER_DB_PASSWORD,
database: process.env.OTHER_DB_NAME,
options: {
instanceName: process.env.OTHER_DB_INSTANCE,
enableArithAbort: false,
},
logging: parseBoolean(process.env.OTHER_DB_LOGGING),
dropSchema: false,
synchronize: false,
migrationsRun: false,
entities: [],
} as TypeOrmModuleOptions,
];
src/app.module.ts
import configuration from '#config/configuration';
import validationSchema from '#config/validation';
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { TypeOrmModule } from '#nestjs/typeorm';
import { LoggerService } from '#shared/logger/logger.service';
import { UsersModule } from '#user/user.module';
import { AppController } from './app.controller';
import ormconfig = require('./config/ormconfig'); //path mapping doesn't work here
#Module({
imports: [
ConfigModule.forRoot({
cache: true,
isGlobal: true,
validationSchema: validationSchema,
load: [configuration],
}),
TypeOrmModule.forRoot(ormconfig[0]), //default
TypeOrmModule.forRoot(ormconfig[1]), //other db
LoggerService,
UsersModule,
],
controllers: [AppController],
})
export class AppModule {}
package.json
"scripts": {
...
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --config ./src/config/ormconfig.ts",
"typeorm:migration:generate": "npm run typeorm -- migration:generate -n",
"typeorm:migration:run": "npm run typeorm -- migration:run"
},
Project structure
src/
├── app.controller.ts
├── app.module.ts
├── config
│ ├── configuration.ts
│ ├── ormconfig.ts
│ └── validation.ts
├── main.ts
├── model
│ ├── entity
│ ├── migration
│ └── repository
├── route
│ └── user
└── shared
└── logger
#DamarOwen's answer worked for me, after I stumbled over another gotcha: If you want to store the connection name as a variable, do not export that constant from app.module.ts. Store the variable in another file like constants.ts.
This is what I tried:
app.module.ts
export const DB_CONNECTION_1 = 'conn1';
#Module({
imports: [
TypeOrmModule.forRootAsync({
name: DB_CONNECTION_1,
...
})
],
...
)
export class AppModule {}
database.module.ts
#Module({
imports: [
TypeOrmModule.forFeature(
[MyRepo],
DB_CONNECTION_1,
),
],
providers: [MyRepo],
})
my.repo.ts
#Injectable()
export class MyRepo {
constructor(
#InjectRepository(MyOrmEntity, DB_CONNECTION_1)
private readonly repo: Repository<MyOrmEntity>,
) {}
}
This threw an Error No repository found for MyOrmEntity was found. Looks like this entity is not registered in current "default" connection. (Note: My application has another connection named "default").
I had to move export const DB_CONNECTION_1 = 'conn1'; away from app.module.ts into its own file constants.ts. Then it worked.
Just a quick PSA, maybe this was missing at the time the question was asked, but now NestJS has complete documentation on this topic, including all the pitfalls that other answers here address: https://docs.nestjs.com/techniques/database#multiple-databases

Categories