How to set a custom unauthorized oauth2 error using NestJS and Passport - exception

I am using a custom passport strategy and I would like to send to the client a custom error If the client denies access to the oauth2 application.
This is the authorization flow:
I would like to send the user a customized error message instead of the 500 error. In case of success, when the user authorizes the application everything goes well.
In the documentation NestJS official documentation there is an example of how I could do this:
https://docs.nestjs.com/security/authentication#extending-guards
import {
ExecutionContext,
Injectable,
UnauthorizedException,
} from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
// Add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
return super.canActivate(context);
}
handleRequest(err, user, info) {
// You can throw an exception based on either "info" or "err" arguments
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}
There is no apparent change and when I analyze the application logs, the only thing that is different is that it raises an exception:
api_1 | [Nest] 5080 - 12/10/2020, 6:13:54 PM [RouterExplorer] Mapped {/users/sign-up/email, POST} route +0ms
api_1 | [Nest] 5080 - 12/10/2020, 6:13:54 PM [RouterExplorer] Mapped {/users/sign-up/confirm-email, GET} route +0ms
api_1 | [Nest] 5080 - 12/10/2020, 6:13:54 PM [NestApplication] Nest application successfully started +3ms
api_1 | [Nest] 5080 - 12/10/2020, 6:14:00 PM [ExceptionsHandler] Object:
api_1 | {
api_1 | "name": "AuthorizationError",
api_1 | "message": "",
api_1 | "code": "access-denied",
api_1 | "status": 500
api_1 | }
api_1 | +6377ms
These are the relevant codes regarding this problem.
meli.strategy.ts
import { Strategy } from 'passport-oauth2';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable } from '#nestjs/common';
import UsersService from '#/users/users.service';
import MeliService from '#/meli/meli.service';
#Injectable()
export class MeliStrategy extends PassportStrategy(Strategy, 'meli') {
constructor(
private usersService: UsersService,
private meliService: MeliService,
) {
super({
authorizationURL: `https://auth.mercadolivre.com.br/authorization?response_type=code&client_id=${process.env.MELI_APP_ID}`,
tokenURL: 'https://api.mercadolibre.com/oauth/token',
clientID: process.env.MELI_APP_ID,
clientSecret: process.env.MELI_APP_SECRET,
callbackURL: process.env.MELI_REDIRECT_URL,
scope: 'authorization_code',
});
}
async validate(accessToken: string) {
const userData = await this.meliService.getUserData(accessToken);
let user = await this.usersService.findUserByMercadoLibre(userData);
if (!user) {
user = await this.usersService.signUpByMercadoLibre(userData);
}
return user;
}
}
export default MeliStrategy;
meli-auth.guard.ts
import {
BadRequestException,
ExecutionContext,
Injectable,
} from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export default class MeliAuthGuard extends AuthGuard('meli') {
canActivate(context: ExecutionContext) {
// Add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
return super.canActivate(context);
}
handleRequest(err, user, info) {
// You can throw an exception based on either "info" or "err" arguments
if (err || !user) {
throw err || new BadRequestException('Just a custom message...');
}
return user;
}
}

Related

How to compare password when using bcrypt nest js log in api in mysql

How I can compare password when signin in using bcrypt,I am facing problem at the time of signin for comparing password.From that select query i can get the matched mailid but how to get hash password?????????????????
note:I am not using typeorm...
Here is my service.ts code,
import { ConflictException, Injectable } from '#nestjs/common';
import { SignInDto,SignUpDto } from '../dto';
import { execute } from '../mysql';
import * as bcrypt from 'bcrypt';
import { FORMERR } from 'dns';
#Injectable()
export class AuthService {
// ------SignUp-------
public async CREATE(Dto: SignUpDto): Promise<any> {
const [account]:any = await execute(
`
SELECT
*
FROM
account
WHERE
email = ? AND
is_active = ? AND
is_deleted = ?
`,
[Dto.email.toLowerCase(), 1, 0],
);
if (account) {
throw new ConflictException('Account already exists on this email id.');
}
Dto.email = Dto.email.toLowerCase();
Dto.password = await bcrypt.hash(Dto.password, 12);
Dto.confirmPassword = await bcrypt.hash(Dto.confirmPassword, 12);
const data = { ...Dto};
return await execute(`INSERT INTO account SET ?`, [data]);
}
// -------SignIn---------
public async GET(Dto: SignInDto): Promise<any> {
const [isExist]:any = await execute(
`
SELECT
*
FROM
account
WHERE
email = ? AND
is_active = ? AND
is_deleted = ?
`,
[Dto.email.toLowerCase(), 1, 0],
);
*if (!isExist) {
const compare=await bcrypt.compare()
throw new ConflictException('Account does not exists.');
}*
return {
id: isExist.id,
};
}
}
conroller.ts
import { Controller, Post, Body, HttpCode, HttpStatus, Res, Get, ParseIntPipe, Param } from '#nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '#nestjs/swagger';
import { SignUpDto, SignInDto } from '../dto';
import { Response } from 'express';
import { AuthService } from './auth.service';
#Controller('auth')
export class AuthController {
constructor(private readonly _authService: AuthService) { }
#Post('/sign-up')
#HttpCode(HttpStatus.OK)
#ApiResponse({ status: HttpStatus.OK, description: 'Success' })
#ApiOperation({ summary: 'SignUp' })
public async SIGNUP(#Res() res: Response, #Body() Dto: SignUpDto): Promise<any> {
const result: any = await this._authService.CREATE(Dto);
if (result) {
return res.status(HttpStatus.OK).json({ status: HttpStatus.OK, message: `Registration completed successfully.` });
}
return res.status(HttpStatus.BAD_REQUEST).json({ status: HttpStatus.BAD_REQUEST, message: `Something went wrong. Please try again later.` });
}
#Post('/sign-in')
#HttpCode(HttpStatus.OK)
#ApiResponse({ status: HttpStatus.OK, description: 'Success.' })
#ApiOperation({ summary: 'SignIn' })
public async SIGNIN(#Res() res: Response, #Body() Dto: SignInDto): Promise<any> {
const result: any = await this._authService.GET(Dto);
if (result) {
res.status(HttpStatus.OK).json({ status: HttpStatus.OK, data: result, message: `Successfull` });
}
}
}
I am facing problem at the time of signin for comparing password.From that select query i can get the matched mailid but how to get hash password?????????????????
Thanks.....
First, there's no need to save the hashed confirmation password. The confirmation password should just be checked that it matches the password, to make sure the user sent in the password they expected to.
Second, assuming you have a password column, you should be able to get the password via isExist.password. Then you can check if the passwords are the same using bcrypt via bcrypt.compare(Dto.password, isExist.password). Bcrypt will take care of computing the same salt based on the hashed password (it's part of the hash actually). The compare method will return a boolean if the passed password hashes to the same hashed value and you can tell then if it was correct or not.

how to handle errors in exception filters with Fastify NestJs?

Im using the following code to catch error in fastify, however my error is that "response.send" is not a function:
What's the right way to send the error on my global exception filters using that structure in Fastify?
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
HttpStatus,
Injectable,
} from '#nestjs/common';
import dotenv from 'dotenv';
import { FastifyReply, FastifyRequest } from 'fastify';
#Catch()
#Injectable()
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const context = host.switchToHttp();
const response: FastifyReply<any> = context.getResponse<FastifyReply>();
const request: FastifyRequest = context.getRequest<FastifyRequest>();
let status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message =
exception instanceof Error ? exception.message : exception.message.error;
response.send({"test":"test"})
}
}
You should add the exception at catch decorator, ie #Catch(HttpException),and without injectable decorator. My snippet for custom http-exception-filter is like below and it works. Hope this help.
#Catch(HttpException)
export class HttpExceptionFilter<T extends HttpException> implements ExceptionFilter
{
catch(exception: T, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response: FastifyReply<any> = ctx.getResponse<FastifyReply>();
const status = exception.getStatus();
const exceptionResponse = exception.getResponse();
const error =
typeof response === 'string'
? { message: exceptionResponse }
: (exceptionResponse as object);
response
.status(status)
.send({ ...error, timestamp: new Date().toISOString() });
}
}

Angular Unexpected token c in JSON at position 0 at JSON.parse when expecting a string

I am not sure what I am doing wrong here.
I am trying to use the checkout facility for stripe using this documentation: https://stripe.com/docs/payments/checkout/accept-a-payment
I have configured my API to just return the checkoutid as a string.
The Angular service just calls the controller. When I run my code I actually get a nice 200 response and I can see the checkout id in the response body, but Angular throws an error:
SyntaxError: Unexpected token c in JSON at position 0 at JSON.parse () at XMLHttpRequest.onLoad (https://127.0.0.1:4200/vendor.js:18780:51) at ZoneDelegate.invokeTask
The service looks like this:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { environment } from '#environments/environment';
#Injectable({
providedIn: 'root',
})
export class StripeService {
private endpoint: string = 'stripe';
constructor(private http: HttpClient) {}
checkout(priceId: string) {
return this.http
.get<string>(`${environment.apiUrl}/${this.endpoint}/${priceId}`)
.pipe(
map((response) => {
console.log(response);
return response;
})
);
}
}
and I am invoking it like this:
this.stripeService
.checkout(this.button.priceId)
.subscribe((checkoutId: string) => {
console.log(checkoutId);
// this.stripe
// .redirectToCheckout({
// sessionId: checkoutId,
// })
// .then(function (result) {
// // If `redirectToCheckout` fails due to a browser or network
// // error, display the localized error message to your customer
// // using `result.error.message`.
// });
});
If I look in the network tab I can see this:
But the console actually shows this:
Does anyone have a scooby why?
Probably the response is a string and you haven't specified the response type. Try the following
this.http.get(
`${environment.apiUrl}/${this.endpoint}/${priceId}`,
{ responseType: 'text' }
)
Default response type is json.
It happened to me when my API return doesent match with my deserializable object on Angular. At first, try to check your returns entities

Angular 7 HttpClient post can not parse large request body?

Does angular HttpClient have limit on request body size?
I was doing an angular project that consumes a rest API. The API is simple, it's just replace a JSON file whenever the API endpoint to the file hits using POST method, with new JSON object as the request body.
The code runs well if the new JSON object is small and only have some nodes, However the code returns an error with status code 400 and error message "Error when parsing request" when i'm trying to send big object. Does the error caused by the big object or am i doing it incorrectly?
Last time it returns error is when i tried to send big JSON file with ~1.5MB size, the JSON object seems to be valid as several online json formatters report it as a valid json object.
I'm sorry if i didn't explain my problem well, as i'm not native english user, and i'm also quite new in angular world. Thanks
import { HttpClient, HttpHeaders, HttpErrorResponse } from "#angular/common/http";
import { take, catchError } from "rxjs/operators";
import { throwError, Observable } from "rxjs";
import { Injectable } from "#angular/core";
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json',
'Cache-Control': 'no-cache',
'Access-Control-Allow-Origin': '*'
});
#Injectable()
export class configService {
private _url = 'http://some-api-endpoint/';
constructor(private http:HttpClient) { }
private _save(data):Promise<any> {
return this.http.post(this._url, data, { headers, responseType: 'text', observe:'response' })
.pipe(
take(1),
catchError(err => this._handleError(err))
)
.toPromise();
}
public async updateConfigFile(data): Promise<any> {
try {
const json = JSON.stringify(data, null, 2);
const update = await this._save(json);
if(update.status === 201) {
return {
success: true,
data: update
}
} else {
return {
success: false,
error: update
}
}
} catch(err) {
return {
success: false,
error: err.message
}
}
}
private _handleError(error: HttpErrorResponse): Observable<never>{
let errObj = {};
if(error.error instanceof ErrorEvent) {
const err = error.error
console.warn('An client-side error occured:', {err});
errObj = {
status: 'internal error',
message: error.error.message
}
} else {
console.warn(`Backend returned code ${error.status} `, {error});
errObj = {
status: error.status,
message: error.error
}
}
return throwError(errObj);
}
}

Angular 4 custom http interceptor. localStorage.getItem() returns null

I am using Azure B2C authentication. Upon successful redirect, the access token gets stored in browser's localStorage and for subsequent API calls, following http interceptor class is supposed to attach the auth token to all outbound requests. Issue is that localStorage.getItem() returns null when trying to read auth token from localStorage. Here is the code,
import { HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest }
from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class HttpManagerInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = req.clone({ headers: req.headers.set('Accept', 'application/json') });
req = req.clone({ headers: req.headers.set('X-CRSP-TOKEN', 'ToBeImplemented') });
// this line always returns null
const authToken = window.localStorage.getItem('auth_token');
console.log('Inside http interceptor. Access token: ' + authToken);
if (authToken) {
req = req.clone({ headers: req.headers.set('Authorization', `Bearer
${authToken}`) });
}
console.log(JSON.stringify(req.headers));
return next.handle(req);
}
Console logs
Token found:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYmY5Njg3YWYtOTliMy00YzU3LWI2YjAtOWE5OGIzNTRhOWQyL3YyLjAvIiwiZXhwIjoxNTA0MTMxNzM3LCJuYmYiOjE1MDQxMjgxMzcsImF1ZCI6IjI4ZGM0NjZkLWRhZGUtNDNkMy04ZjBhLTJkYmNlNTQxYmIxMyIsIm9pZCI6IjcyMzljZWVjLTMzN2ItNDlmNS04YzViLTVkMzcwZGEwZmIxOCIsImdpdmVuX25hbWUiOiJaZWVzaGFuIiwiZmFtaWx5X25hbWUiOiJIYWlkZXIiLCJzdWIiOiJOb3Qgc3VwcG9ydGVkIGN1cnJlbnRseS4gVXNlIG9pZCBjbGFpbS4iLCJlbWFpbHMiOlsiWmVlc2hhbi5IYWlkZXJAY3JzcC5jaGljYWdvYm9vdGguZWR1Il0sImF6cCI6IjI4ZGM0NjZkLWRhZGUtNDNkMy04ZjBhLTJkYmNlNTQxYmIxMyIsInZlciI6IjEuMCJ9.DUebFoHuzLXIbjMOmRrCRYswMB1g-7J6kVOaYyI3-b5AuaTjrcTtTsZkiGbloseaKqKtKoRtO72EkyQ2XvJ2lyhCBybpD4skeOcwQ2p_RBcO1dlFSoWIOkQK7WPN_f3tLxzuvKgrcPuR2LurB_n0uEq8PTdMIKXgfuCVDUSjxGrcwlzGi61k2g24wzO-u9YdN5Xqx0eFqooE0hhiifTsAsXPNJhXTmLinr4qt25bRfvVs1UpYNk6hv1RQ3afrg7UZavr-Osjh5amQ6Qi_q6kKTQWorB9Cgoj_UTIA8ojkK-6y7D8uzY-YtLzomuNvD8mELCeZC8ZdPbbibzC2Kj6Rw
Inside http interceptor. Access token: null
I am suspecting if INTERCEPTORS initialized or created before localStorage is available to use. If that is the case and there is no workaround, can anyone suggest other solutions?
Your help will be appreciated!
Inject window inside your component
#Inject(WINDOW) private window: any