How to Authenticate and Retrive data Google JWT in NestJs - jwt

I am working on nestjs backend app.I have google access token from google auth playground and i want to retrieve user data from this token using jwt ex. like curl http://locahost:3000/user/profile -H "Authorization : Bearer jwt".I have used jwt strategy from stackoverflow Authenticate Google JWT in NestJs question but unable to get data getting unauthorized error.
Below is my code and jwt strategy
JWT Stragy:
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private configService: ConfigService){
super({
secretOrKeyProvider: passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: 'https://www.googleapis.com/oauth2/v3/certs',
}),
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
issuer: 'https://accounts.google.com',
algorithms: ['RS256'],
});
}
async validate(payload: any): Promise<unknown> {
// const { email } = payload;
console.log('payload t',payload)
return payload;
}
}
App module:
#Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
// JwtModule.register({
// secretOrPrivateKey: 'secretKey',
// signOptions: { expiresIn: '60s' },
// }),
AuthModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Auth module:
#Module({
imports: [
ConfigModule.forRoot(),
],
providers: [JwtStrategy],
exports: [JwtStrategy],
})
export class AuthModule {}

Related

how can i access my env file from a nestjs module using passport js?

I am working on a small api using nestjs and passeport js
I have been trying to access the content of my env file, from within my auth module...but it's surpinsigly challenging...
import { userService } from 'src/user/services/user.service';
import { AuthService } from './auth.service';
import { PassportModule } from '#nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { JwtModule } from '#nestjs/jwt';
import { JwtStrategy } from './jwt.strategy';
import { ConfigService } from '#nestjs/config';
#Module({
providers: [AuthService, userService, LocalStrategy, JwtStrategy],
imports: [
TypeOrmModule.forFeature([UserEntity, SpotEntity, SpotUserEntity]),
UserModule,
PassportModule,
JwtModule.register({
secret: ConfigService.get('JWT_SECRET'),
signOptions: { expiresIn: '600s' },
}),
],
exports: [AuthService],
})
export class AuthModule {}
Off course this cannot work because i am trying to utilize ConfigService.get() instead of this.configService.get()
I know i would need to instanciate configService in a constructor first, but modules do not have constructors, this is where i'm stuck at.
You can try registerAsync i.e
import { ConfigModule, ConfigService } from '#nestjs/config';
......
imports: [
PassportModule.register({
defaultStrategy: 'jwt',
}),
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => {
return {
signOptions: {
expiresIn: config.get<string>('JWT_EXPIRY'),
},
secret: config.get<string>('JWT_SECRET'),
};
},
inject: [ConfigService],
})
]
....

MongoDb's change stream in NestJs

I look around on how mongoDb change stream can be implemented in NestJs but so far, i can't find any solution or documentation.
There is a similar way by using Hooks middleware, but this can't be triggered if we change data from external app.
#Module({
imports: [
MongooseModule.forFeatureAsync([
{
name: Cat.name,
imports: [ConfigModule],
useFactory: (configService: ConfigService) => {
const schema = CatsSchema;
schema.post('save', function() {
console.log(
`${configService.get('APP_NAME')}: Hello from post save`,
),
});
return schema;
},
inject: [ConfigService],
},
]),
],
})
export class AppModule {}
I found this document https://www.mongodb.com/docs/manual/changeStreams/ but how can we implement this in NestJs?
Something like this should work. I wasn't able to test this since I don't have a replica set set up.
#Injectable()
export class ChangeStreamService implements OnModuleInit {
constructor(#InjectModel(Schema.name) private schemaModel: Model<SchemaDocument>) {}
onModuleInit() {
this.schemaModel.collection.watch<SchemaDocument>().on('change', (e) => {
console.log(e)
})
}
}

How to implement Facebook OIDC login on Next.js with NextAuth?

I have implemented a regular (Auth2) Facebook login with NextAuth that works fine, but now, I have to switch to OIDC login and can't seem to implement it properly. I'm getting the following error message:
[token_endpoint must be configured on the issuer](https://next-auth.js.org/errors#callback_oauth_error token_endpoint must be configured on the issuer TypeError: token_endpoint must be configured on the issuer)
And this is my configuration on the Facebook Provider:
export default NextAuth({
providers: [
FacebookProvider({
idToken: true,
clientId: process.env.FBID,
clientSecret: process.env.FBSECRET,
wellKnown: "https://www.facebook.com/.well-known/openid-configuration",
token: {
url: "https://www.facebook.com/v11.0/dialog/oauth",
params: { scope: "openid email public_profile" },
},
}),
],
secret: "sunSAd2RkCajg2DLR3+5MfsinFwws8ZuzfPm2C+FXkc=",
});
Try this code
providers: [
FacebookProvider({
idToken: true,
clientId: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
authorization: {
url: "https://www.facebook.com/v11.0/dialog/oauth",
params: {
client_id: process.env.FACEBOOK_CLIENT_ID,
scope: "openid email",
response_type: "code",
},
},
// wellKnown: "https://www.facebook.com/.well-known/openid-configuration/",
token: {
url: "https://graph.facebook.com/oauth/access_token",
async request(context) {
try {
// request to https://graph.facebook.com/oauth/access_token?code=""&client_id=""&redirect_uri=""&client_secret=""
const response = await axios.get(this.url, {
params: {
code: context.params.code,
client_id: context.provider.clientId,
redirect_uri: context.provider.callbackUrl,
client_secret: context.provider.clientSecret,
},
});
const tokens = response.data;
return { tokens };
} catch (error) {
throw error;
}
},
},
}),
]

JWT not expiring in nestjs application even after setting expiresIn value

This is what my auth.module.ts looks like:
import { Module } from "#nestjs/common";
import { ConfigModule, ConfigService } from "#nestjs/config";
import { JwtModule } from "#nestjs/jwt";
import { PassportModule } from "#nestjs/passport";
import { TypeOrmModule } from "#nestjs/typeorm";
import appConfig from "src/config/app.config";
import devConfig from "src/config/dev.config";
import stagConfig from "src/config/stag.config";
import { User } from "src/entities/entity/user.entity";
import { AuthService } from "./auth.service";
import { JwtStrategy } from "./passport-strategies/jwt-strategy";
import { LocalStrategy } from "./passport-strategies/local-strategy";
#Module({
imports: [
PassportModule,
ConfigModule.forRoot({
load: [appConfig, devConfig, stagConfig],
ignoreEnvFile: true,
isGlobal: true,
}),
TypeOrmModule.forFeature([
User
]),
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
// secret: configService.get<string>('jwt.secret'),
secret: process.env.TOKEN_KEY,
signOptions: { expiresIn: 30 }
}),
inject: [ConfigService]
}),
],
providers: [
AuthService,
LocalStrategy,
JwtStrategy
],
exports: [AuthService],
})
export class AuthModule {}
As you can see I have set signOptions: { expiresIn: 30 } but when I analyze the token it has no expiration parameter and does not expire.
I am using https://jwt.io/#encoded-jwt to analyze the token:
You have to pass the expiresIn value as string and mention s for seconds like this
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: '60s' },
}),
Do let me know if this works!
The same problem. search ignoreExpiration flag. Here's my problem
e:
#Injectable()
export class JWTStrategy extends PassportStrategy(Strategy) {
constructor(
readonly configService: ConfigService,
private userService: UserService,
) {
super({
jwtFromRequest: ExtractJwt.fromHeader(
configService.get('API_ACCESS_TOKEN_HEADER'),
),
ignoreExpiration: true, // *** FIXME: here
secretOrKey: configService.get('API_ACCESS_TOKEN_SECRET'),
});
}
...
Here is type:
export interface StrategyOptions {
secretOrKey?: string | Buffer | undefined;
secretOrKeyProvider?: SecretOrKeyProvider | undefined;
jwtFromRequest: JwtFromRequestFunction;
issuer?: string | undefined;
audience?: string | undefined;
algorithms?: string[] | undefined;
ignoreExpiration?: boolean | undefined;
passReqToCallback?: boolean | undefined;
jsonWebTokenOptions?: VerifyOptions | undefined;
}
The ignoreExpiration used, https://github.com/auth0/node-jsonwebtoken/blob/master/verify.js#L147
Hope it will help you

NestJs: Query multiple entities from multiple database

I have mysql_server_1.database1.users
And mysql_server_2.database3.users_revenue
How can I query rows from users
How can I query rows from users_revenue
First, I've already setup the connections:
const mysql1__database1 = TypeOrmModule.forRootAsync({
imports: [ConfigModule],
// #ts-ignore
useFactory: (configService: ConfigService) => ({
type: configService.get("DASHBOARD_DB_TYPE"),
host: configService.get("DASHBOARD_DB_HOST"),
port: configService.get("DASHBOARD_DB_PORT"),
username: configService.get("DASHBOARD_DB_USER"),
password: configService.get("DASHBOARD_DB_PASSWORD"),
database: configService.get("DASHBOARD_DB_NAME"),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
// entities: [User],
autoLoadEntities: true,
synchronize: true,
}),
inject: [ConfigService],
});
const mysql2__database3 = TypeOrmModule.forRootAsync({
imports: [ConfigModule],
// #ts-ignore
useFactory: (configService: ConfigService) => ({
name: 'mysql2__database3',
type: configService.get("DASHBOARD2_DB_TYPE"),
host: configService.get("DASHBOARD2_DB_HOST"),
port: configService.get("DASHBOARD2_DB_PORT"),
username: configService.get("DASHBOARD2_DB_USER"),
password: configService.get("DASHBOARD2_DB_PASSWORD"),
database: configService.get("DASHBOARD2_DB_NAME"),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
// entities: [User],
autoLoadEntities: true,
synchronize: true,
}),
inject: [ConfigService],
});
#Module({
imports: [
mysql1__database1,
mysql2__database3,
StatsModule,
],
controllers: [AppController],
providers: [AppService, StatsService],
})
export class AppModule {}
user.service.ts
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
#Injectable()
export class UserService {
constructor(#InjectRepository(User) private usersRepository: Repository<User>) {}
async findAll(): Promise<User[]> {
return await this.usersRepository.find();
}
}
Then this code return an empty array instead of so many rows exists in my database;
const items = await this.userService.findAll();
--- update ---
I've take a look at the typeorm source code:
https://github.com/nestjs/typeorm/blob/8af34889fa7bf14d7dc5541beef1d5c2b50c2609/lib/common/typeorm.decorators.ts#L13
Then https://docs.nestjs.com/techniques/database#multiple-databases
At this point, you have User and Album entities registered with their own connection. With this setup, you have to tell the TypeOrmModule.forFeature() method and the #InjectRepository() decorator which connection should be used. If you do not pass any connection name, the default connection is used.
So I think it should work?
#InjectRepository(User, 'mysql2_database3')
#Module({
imports: [
TypeOrmModule.forFeature([User], "mysql2_database3"),
],
providers: [UserService],
controllers: [StatsController],
})
export class StatsModule {}
Still got the error:
Please make sure that the argument mysql2_database3Connection at index [0] is available in the TypeOrmModule context.
Thank to #jmc29 on discord, his guide helped
The solution is:
const mysql2__database3 = TypeOrmModule.forRootAsync({
imports: [ConfigModule],
// #ts-ignore
useFactory: (configService: ConfigService) => ({
name: 'mysql2__database3',
type: configService.get("DASHBOARD2_DB_TYPE"),
host: configService.get("DASHBOARD2_DB_HOST"),
port: configService.get("DASHBOARD2_DB_PORT"),
username: configService.get("DASHBOARD2_DB_USER"),
password: configService.get("DASHBOARD2_DB_PASSWORD"),
database: configService.get("DASHBOARD2_DB_NAME"),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
autoLoadEntities: true,
synchronize: true,
}),
inject: [ConfigService],
});
add one more line:
const mysql2__database3 = TypeOrmModule.forRootAsync({
name: 'mysql2__database3', // -----> Add this line, it's is required
imports: [ConfigModule],
// #ts-ignore
useFactory: (configService: ConfigService) => ({
name: 'mysql2__database3',
type: configService.get("DASHBOARD2_DB_TYPE"),
host: configService.get("DASHBOARD2_DB_HOST"),
port: configService.get("DASHBOARD2_DB_PORT"),
username: configService.get("DASHBOARD2_DB_USER"),
password: configService.get("DASHBOARD2_DB_PASSWORD"),
database: configService.get("DASHBOARD2_DB_NAME"),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
autoLoadEntities: true,
synchronize: true,
}),
inject: [ConfigService],
});
for those who face this problem , this is my solution
AppModule
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [
database,
databaseAllo
]
}),
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)
}
}
so in your case, you need to call "mysql2_database3" again in your providers: [UserService]