Property 'pipe' does not exist on type 'Promise - nestjs-jwt

I`m developing auth guard in nestjs, I got This error.
If I call .pipe()
async validateUser(email: string, password: string): Promise<User> {
const findUser = await this.userRepository.findOneBy({email: email, password: password });
if (!findUser) throw new NotFoundException("user not found");
return findUser;
}
login(user: User): Observable<string> {
const { email, password } = user;
return this.validateUser(email, password).pipe(
switchMap((user: User) => {
if (user) {
// create JWT - credwntials
return from(this.jwtServices.signAsync({ user }));
}
}),
);
}
This my authService.

You need transform promise to observable using from()
import { from } from 'rxjs';
login(user: User): Observable<string> {
const { email, password } = user;
return from(this.validateUser(email, password)).pipe(
switchMap((user: User) => {
if (user) {
// create JWT - credwntials
return from(this.jwtServices.signAsync({ user }));
}
}),
);

Related

Nextjs getServerSideProps does not pass session to component

Good morning. I am using NextJS and get the session in the getServerSideProps block.
I am passing multiple parameters to the return object, however, the session does not pass. All of the other key value pairs works, but when I try to log the session, It comes undefined... I don't understand...
const DashboardPage = ({ session, secret }) => {
const [loading, setloading] = useState(false);
//This does not work
console.log('Session: ', session);
return (
<section className="border-4 border-orange-800 max-w-5xl mx-auto">
<CreateListModal userId={session?.userId} loading={loading} setloading={setloading} />
</section>
)
}
export const getServerSideProps = async context => {
// get sessions with added user Id in the session object
const session = await requireAuthentication(context);
// This works
console.log(session);
if (!session) {
return {
redirect: {
destination: '/signup',
permanent: false
}
}
}
else {
return {
props: {
session: session,
secret: 'Pasarika este dulce'
}
}
}
}
export default DashboardPage;
The purpose of the requireAuthentication function is to create a new session object where I will insert another key value pair that will contain the user id and the session that will be used in the entire app.
In that function I get the user by email that is returned by the session and get the Id from the db. I than return the new session object that looks like this:
{
user: {
name: 'daniel sas',
email: 'email#gmail.com',
image: 'https://lh3.googldeusercontent.com/a/A6r44ZwMyONqcfJORNnuYtbVv_LYbab-wv5Uyxk=s96-c',
userId: 'clbcpc0hi0002sb1wsiea3q5d'//This is the required thing in my app
},
expires: '2022-12-23T08:04:08.263Z'
}
The following function is used to get the data from the database
import { getSession } from "next-auth/react";
import prisma from './prisma'
// This function get the email and returns a new session object that includes
// the userId
export const requireAuthentication = async context => {
const session = await getSession(context);
// If there is no user or there is an error ret to signup page
if (!session) return null
// If the user is not found return same redirect to signup
else {
try {
const user = await prisma.user.findUnique({where: { email: session.user.email }});
if (!user) return null;
// Must return a new session here that contains the userId...
else {
const newSession = {
user: {
...session.user,
userId: user.id
},
expires: session.expires
};
return newSession;
}
}
catch (error) {
if (error) {
console.log(error);
}
}
}
}

Graphql/bcrypt error "Illegal arguments: string, function"

I am working with graphql to signup/sign in. Registration runs smoothly but I am running into this the bcrypt error upon logging back in. When I change it to user.password in the if statement below the loginUser function, it says throws a newUser is undefined error. Where is my error in this one?
Resolvers:
const resolvers = {
Mutation: {
async registerUser(_, { registerInput: { username, email, password } }) {
const previousUser = await User.findOne({ email });
if (previousUser) {
throw new ApolloError(
"A user with this email already exists" + email,
"User_Already_Exists"
);
}
var encryptedPassword = await bcrypt.hash(password, 10);
const newUser = new User({
username: username,
email: email.toLowerCase(),
password: encryptedPassword,
});
const token = jwt.sign(
{ user_id: newUser._id, email },
"this is the secret",
{
expiresIn: "2h",
}
);
newUser.token = token;
const res = await newUser.save();
return {
id: res.id,
...res._doc,
};
},
async loginUser(_, { loginInput: { email, password } }) {
const user = await User.findOne({ email });
if (user && (await bcrypt.compare(password, user.model))) {
const token = jwt.sign(
{ user_id: newUser._id, email },
"this is the secret",
{
expiresIn: "2h",
}
);
user.token = token;
return {
id: res.id,
...res._doc,
};
You need to compare to user.password not user.model
newUser isn't defined if the passwords match, use user instead.
async loginUser(_, { loginInput: { email, password } }) {
const user = await User.findOne({ email });
if (user && (await bcrypt.compare(password, user.password))) {
const token = jwt.sign(
{ user_id: user._id, email },
"this is the secret",
{ expiresIn: "2h"}
);
user.token = token; // this isn't used anywhere, why?
return {id: res.id,...res._doc}; // this looks wrong too, res is not in scope
}
}

How to add custom header in NestJS GraphQL Resolver

I want to add custom header in the response of one of my GraphQL resolver Queries in Nestjs. here is the code.
#Query(() => LoginUnion)
async login(
#Args({ type: () => LoginArgs }) { LoginInfo: { email, password } } : LoginArgs
): Promise<typeof LoginUnion> {
try {
let userInfo = await this.userModel.findOne({ email, password: SHA256(password).toString() }, { 'password': false });
if (userInfo === null) {
return new CustomErrorResponse({ message: 'Wrong password or username'});
}
if (!userInfo.isVerified) {
return new CustomErrorResponse({ message: 'User in not verified'});
}
const token = await this.authentication.sign(userInfo, config.get('Secure_JWT_Sign_Key'));
// set header here
console.log('_DEBUG_ =>', token);
} catch(error) {
console.log('user.resolver.login => ', error);
return new CustomErrorResponse({ message: 'Server Exception', additonalInfo: JSON.stringify(error) });
}
}

NestJS/GraphQL/Passport - getting unauthorised error from guard

I'm trying to follow along with this tutorial and I'm struggling to convert the implementation to GraphQL.
local.strategy.ts
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authenticationService: AuthenticationService) {
super();
}
async validate(email: string, password: string): Promise<any> {
const user = await this.authenticationService.getAuthenticatedUser(
email,
password,
);
if (!user) throw new UnauthorizedException();
return user;
}
}
local.guard.ts
#Injectable()
export class LogInWithCredentialsGuard extends AuthGuard('local') {
async canActivate(context: ExecutionContext): Promise<boolean> {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();
req.body = ctx.getArgs();
await super.canActivate(new ExecutionContextHost([req]));
await super.logIn(req);
return true;
}
}
authentication.type.ts
#InputType()
export class AuthenticationInput {
#Field()
email: string;
#Field()
password: string;
}
authentication.resolver.ts
#UseGuards(LogInWithCredentialsGuard)
#Mutation(() => User, { nullable: true })
logIn(
#Args('variables')
_authenticationInput: AuthenticationInput,
#Context() req: any,
) {
return req.user;
}
mutation
mutation {
logIn(variables: {
email: "email#email.com",
password: "123123"
} ) {
id
email
}
}
Even the above credentials are correct, I'm receiving an unauthorized error.
The problem is in your LogInWithCredentialsGuard.
You shouldn't override canAcitavte method, all you have to do is update the request with proper GraphQL args because in case of API request, Passport automatically gets your credentials from req.body. With GraphQL, execution context is different, so you have to manually set your args in req.body. For that, getRequest method is used.
As the execution context of GraphQL and REST APIs is not same, you have to make sure your guard works in both cases whether it's controller or mutation.
here is a working code snippet
#Injectable()
export class LogInWithCredentialsGuard extends AuthGuard('local') {
// Override this method so it can be used in graphql
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const gqlReq = ctx.getContext().req;
if (gqlReq) {
const { variables } = ctx.getArgs();
gqlReq.body = variables;
return gqlReq;
}
return context.switchToHttp().getRequest();
}
}
and your mutation will be like
#UseGuards(LogInWithCredentialsGuard)
#Mutation(() => User, { nullable: true })
logIn(
#Args('variables')
_authenticationInput: AuthenticationInput,
#Context() context: any, // <----------- it's not request
) {
return context.req.user;
}
I've been able to get a successful login with a guard like this:
#Injectable()
export class LocalGqlAuthGuard extends AuthGuard('local') {
constructor() {
super();
}
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
const req = ctx.getContext().req;
req.body = ctx.getArgs();
return req;
}
async canActivate(context: ExecutionContext) {
await super.canActivate(context);
const ctx = GqlExecutionContext.create(context);
const req = ctx.getContext().req;
await super.logIn(req);
return true;
}
}

Call redux action in axios interceptor

I store logged in user data in localstorage. I also validate JWT token in axios interceptor and if it's expired I will refresh it. so I need to update store with new user data and JWT token and in order to do that, I need to call redux action that I have in my Auth module.
AuthRedux.js
export const actionTypes = {
Login: "[Login] Action",
Logout: "[Logout] Action",
UserRequested: "[Request User] Action",
UserLoaded: "[Load User] Auth API",
SetUser: "[Set User] Action",
};
const initialAuthState = {
user: undefined,
authToken: undefined,
};
export const reducer = persistReducer(
{ storage, key: "myapp-auth", whitelist: ["user", "authToken"] },
(state = initialAuthState, action) => {
switch (action.type) {
case actionTypes.Login: {
const { authToken } = action.payload;
return { authToken, user: undefined };
}
case actionTypes.Logout: {
// TODO: Change this code. Actions in reducer aren't allowed.
return initialAuthState;
}
case actionTypes.UserLoaded: {
const { user } = action.payload;
return { ...state, user };
}
case actionTypes.SetUser: {
const { user } = action.payload;
return { ...state, user };
}
default:
return state;
}
}
);
export const actions = {
login: (authToken) => ({ type: actionTypes.Login, payload: { authToken } }),
logout: () => ({ type: actionTypes.Logout }),
requestUser: (user) => ({ type: actionTypes.UserRequested, payload: { user } }),
fulfillUser: (user) => ({ type: actionTypes.UserLoaded, payload: { user } }),
setUser: (user) => ({ type: actionTypes.SetUser, payload: { user } }),
};
export function* saga() {
yield takeLatest(actionTypes.Login, function* loginSaga() {
yield put(actions.requestUser());
});
yield takeLatest(actionTypes.UserRequested, function* userRequested() {
const { data: user } = yield getUserByToken();
yield put(actions.fulfillUser(user));
});
}
AxiosInterceptor.js
export default function setupAxios(axios, store, props) {
axios.interceptors.request.use(
config => {
const {
auth: { authToken }
} = store.getState();
if (authToken) {
config.headers.Authorization = `Bearer ${authToken}`;
}
return config;
},
err => Promise.reject(err)
);
axios.interceptors.response.use(
(response) => {
console.log(props);
return response;
},
function (error) {
const originalRequest = error.config;
if (error.response?.status === 401) {
if (error.response.data === "refresh_token") {
// refresh token and set new user data
// question is how can I call redux setUser action in here and update state
}
else if (error.response.data === "invalid_token") {
window.localStorage.clear();
window.location.href = '/auth/login';
}
else { }
}
if (!originalRequest._retry) {
originalRequest._retry = true;
return axios(originalRequest);
}
return Promise.reject(error);
}
);
}
My question is how can I call reducer setUser action in interceptor and update state
You can dispatch actions from outside of a component when you have access to the store with store.dispatch(anAction), in your case you can do:
store.dispatch(setUser())

Resources