i18n-backend with oauth2 authorization in spartacus - spartacus-storefront

We want to use a backend for i18n in spartacus. Unfortunately this backend needs an oauth2 authentication but spartacus does not send a bearer token when trying to access this webservice endpoint and we get a 401 error. Is there anything we can do?
Right now we try to solve this problem in this way:
What we need to have is implemented in ClientTokenInterceptor, so we adapted this interceptor, changed the if-clause a little bit so it fits to the backend-url for this webservices and provide the interceptor via app.module.ts which works so far. Unfortunately calling this.authService.getClientToken() in our Interceptor returns no token.
constructor(
private authService: AuthService,
private occEndpoints: OccEndpointsService
) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return this.getClientToken(request).pipe(
take(1),
switchMap((token: ClientToken) => {
if (
token &&
request.url.includes("i18n")
) {
request = request.clone({
setHeaders: {
Authorization: `${token.token_type} ${token.access_token}`,
},
});
}
return next.handle(request);
})
);
}
private getClientToken(request: HttpRequest<any>): Observable<ClientToken> {
if (
InterceptorUtil.getInterceptorParam(USE_CLIENT_TOKEN, request.headers)
) {
return this.authService.getClientToken();
}
return of(null);
}
What do we miss?

Actually there are couple of things not needed in your solution.
I pasted below what I did instead and tested that it is working correctly (and you can see authorization data in the translation files requests).
First issue:
InterceptorUtil.getInterceptorParam(USE_CLIENT_TOKEN, request.headers) you don't need to check that. If you always need the auth data for translation requests just use return this.authService.getClientToken();
Second issue:
In intercept method you didn't cover cases for any other request than translation. Because of that the request for the client token would hang here, because it would wait for token and so on. If you add option for any other case than i18n it starts working as you intend.
Working solution:
#Injectable({ providedIn: 'root' })
export class TranslationsInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (request?.url?.includes('i18n')) {
return this.getClientToken().pipe(
take(1),
switchMap((token: ClientToken) => {
if (token) {
request = request.clone({
setHeaders: {
Authorization: `${token.token_type} ${token.access_token}`,
},
});
}
return next.handle(request);
})
);
} else {
return next.handle(request);
}
}
private getClientToken(): Observable<ClientToken> {
return this.authService.getClientToken();
}
}

Related

How can I return UUID using RabbitMQ

I would like to return the UUID(v4) created for each request in nestjs as a response to the request.
I want to return the UUID(v4) that nestjs creates for each request as the response to the request.
However, I am using rabbitmq.
Is there any other way to return the UUID after going through rabbitmq for each request?
With this method, I am creating a connection for each request.
I want to create the connection only once after nestjs is started.
Also, any other method using other libraries would be good.
import { Controller, Get } from '#nestjs/common';
import { v4 as uuidv4 } from 'uuid';
#Controller('cats')
export class CatsController {
#Get()
findAll(): string {
const sequence = uuidv4(); // I want to return this.
return 'This action returns all cats';
}
}
...
await channel.consume(queueName, async (data: any) => {
if (queueName === 'testQueue') {
// do something.
}
});

How can we access decorator from a service class in NestJS

I am new in NestJS and trying to do auth system. I was able to do. So here is what I am doing to get access to auth.
In my controller I have
#Get('/user')
async getUser(#AuthUser() token: string) : Promise<Object> {
return this.authService.getUser(token)
return token
}
Here I am passing a AuthUser decorator I want to avoid passing in controllers.
In the authService.getUser method I have something like this
async getUser(token: string): Promise<Object> {
try {
const user = await this.jwtService.verifyAsync(token)
return user
} catch (error) {
return false
}
}
and my decorator looks like this
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
export const AuthUser = createParamDecorator(
(data = 'u_ses', ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return data ? request.cookies?.[data] : request.cookies;
},
);
I don't like code. If I need to know the user id from a service class or anywhere I would need to pass the token and to get token I need use #AuthUser() token: string)
So I want to do something like this
this.authService.getUser(), here I don't want to pass token or anything and should be able to access this getUser method from anywhere. Since it's a service class, I can inject and use it but I won't have the token.
I tried injecting the decorator inside the service class, but this doesn't work.
One best solution I would prefer is to use the JWT things inside the decorator, so I don't need the service class' method :)
I am looking for a nicer solutions from you :)
Thank you.
Nestjs has NestMiddleware. Here, you can authorize before access to controller like this:
import { Injectable, NestMiddleware, UnauthorizedException } from '#nestjs/common';
import { Request, Response, NextFunction } from 'express';
#Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const headerAuthentication = req.headers.authorization;
if(!headerAuthentication) throw new UnauthorizedException('Authorization failed!');
const token = req.headers.authorization.split(' ')[1];
if(token) {
next();
}else {
throw new UnauthorizedException('Authorization failed!');
}
}
}
and in AppModule implement it
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthenticationMiddleware).forRoutes('/');
}
}

Resend unathorized requests after changing token in Angualr8

I'm new to angular 8.
I have an interceptor :
export class HttpRequestInterceptor implements HttpInterceptor {
private apiAddress = 'http://localhost:1080';
private refreshTokenIsInProgress = false;
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
let cloneReq;
//...
// some codes like adding access token to header
//...
cloneReq = request.clone();
return next.handle(cloneReq)
.pipe(
catchError((error: any) => {
if(error.status==401)//means token expired
{
//Here i need help
//Get NEW Token And Replace With previous And Resend Current Request
}
return of(error);
})
)
;
}
}
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: HttpRequestInterceptor, multi: true },
];
my requests are like:
return this.http.get(url).subscribe();
As I mentioned,if request returns 401(unauthorized) I need to get new token (JWT) from server , replace it with previous one, and then resend CURRENT request;
there is no problem on getting new token!
the problem is replacing new token and resend request;
i searched and googled form many hours but ...
i thought i can reach it by using retry() and retryWhen() , but they can't change request parameters.
thank you all buddy
Haven't tested the code, but you can try wrap the request into higher order function and get access to request object, also allow function to be called recursively until condition is met.
const repeatRequest=(cloneReq)=>next.handle(cloneReq)
.pipe(
catchError((error: any) => {
if(error.status==401)//means token expired
req=........ // modify your request here
return repeatRequest(req)
return of(error);
})
)
cloneReq = request.clone();
return repeatRequest(cloneReq)

Customise the response on verification failure for a jwt Strategy NestJs

I successfully implemented a jwt strategy for authentication using nestJs.
Below is the code for the jwt strategy
import { ServerResponse } from './../helpers/serverResponse.helper';
import { Injectable, UnauthorizedException, HttpStatus } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { config as env } from 'dotenv';
import { Bugsnag } from '../helpers/bugsnag.helper';
env();
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(
private readonly logger: Bugsnag,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET_KEY,
passReqToCallback: true,
});
}
async validate(payload, done: Function) {
try {
const validClaims = await this.authService.verifyTokenClaims(payload);
if (!validClaims)
return done(new UnauthorizedException('invalid token claims'), false);
done(null, payload);
} catch (err) {
this.logger.notify(err);
return ServerResponse.throwError({
success: false,
status: HttpStatus.INTERNAL_SERVER_ERROR,
message: 'JwtStrategy class, validate function',
errors: [err],
});
}
}
}
I saw here that the validate function will be called only when a valid token was provided in the request headers and I'm okay with that. However, I would like to know if it is possible to customize the response object which is sent in that case (invalid token provided).
If yes, how do I do that ?
You can use a exception filter to catch UnauthorizedExceptions and modify the response there if you'd like. The other option would be extending the AuthGuard('jwt') mixin class and adding in some logic around a try/catch for the super.canActivate(context), then in the error read what the reason is and throw a specific UnauthorizedException with your custom message
You can use the AuthGuard('jwt')'s handleRequest method to throw any exception on JWT Validation failure.
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
handleRequest(err: any, user: any, info: any, context: any, status: any) {
if (info instanceof JsonWebTokenError) {
throw new UnauthorizedException('Invalid Token!');
}
return super.handleRequest(err, user, info, context, status);
}
}
JsonWebTokenError comes from jsonwebtoken library, which is used internally by passport.

Setting Headers (token) of multiple objects once user is authenticated in Angular2

Hi I've created a generic service from which I can create objects with generic http requests.
The challenge I'm facing now is to pass to each created object a token into their headers if the user is authenticated (i will get a token as response, which is stored in the localStorage)
So basically I can create these custom http objects anywhere (component-wise by injection) on any level. All of them don't have the Authentication Header set yet. Once User is authenticated, all these object to have their Authentication Header set.
Here's the plunker
export class App {
myHttpObject1;
constructor(private myAuth:MyAuth, private myDatabase:MyDatabase) {
this.name = 'Angular2 (Release Candidate!)'
this.myHttpObject1 = this.myDatabase.httpSchema('users')
this.myHttpObject1.log()
// this.myHttpObject1.someOtherMethodes()...
}
login(){
this.myAuth.login()
}
showHeaders(){
this.myHttpObject1.log()
}
}
Below is the Service and one to mock a login.
#Injectable()
export class MyDatabase{
private base_url:string;
private headers :Headers;
constructor(){
this.base_url = 'https://jsonplaceholder.typicode.com/';
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Authorization','');
}
public httpSchema(path:string){
return new MyHttpObject(path, this.headers || new Headers())
}
}
class MyHttpObject{
constructor(public url:string, public headers:Headers){
}
log(){
console.log(this.url)
console.log(this.headers)
}
post(){
console.log('here could be a http post')
}
}
#Injectable()
export class MyAuth{
login(){
setTimeout(()=>{
console.log('logged In');
localStorage.setItem('token':'mytoken');
},2000)
}
}
Look at the Http Injector, it intercepts the call and adds whatever you need to the http object.