Home > Software engineering >  Bypassing BearerStrategy in Nestjs Guard and Strategy
Bypassing BearerStrategy in Nestjs Guard and Strategy

Time:11-12

I have the following Nestjs global guard for my app:

import { ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { AuthGuard } from "@nestjs/passport";

@Injectable()
export class LoginGuard extends AuthGuard(
    ["azure-ad"]
) {
    public constructor(private readonly reflector: Reflector) {
        super();
    }

    async canActivate(context: ExecutionContext) {
        const isPublic = this.reflector.get<boolean>(
            "isPublic",
            context.getHandler()
        );

        if (isPublic) {
            return true;
        }
        
        const result = (await super.canActivate(context)) as boolean;
        const request = context.switchToHttp().getRequest();
        await super.logIn(request);

        return result;
    }
}

And the following strategy for auth:

import {
  BearerStrategy,
  IBearerStrategyOption,
  ITokenPayload,
  VerifyCallback,
} from "passport-azure-ad"
import {
  Inject,
  Injectable,
  OnModuleInit,
  UnauthorizedException,
} from "@nestjs/common"
import passport = require("passport")
import env from "../../../config"
import { User } from "modules/users/users.interface"
import { UsersService } from "modules/users/users.service"

const tenantId = env.TENANT_ID
const clientID = env.CLIENT_ID || ""

const azureCredentials: IBearerStrategyOption = {
  identityMetadata: `https://login.microsoftonline.com/${tenantId}/v2.0/.well-known/openid-configuration`,
  clientID,
  validateIssuer: true,
  issuer: `https://login.microsoftonline.com/${tenantId}/v2.0`,
  audience: clientID,
}

@Injectable()
export class AzureAdStrategy extends BearerStrategy implements OnModuleInit {
  onModuleInit() {
    passport.use("azure-ad", this)
  }

  constructor(@Inject(UsersService) private usersService: UsersService) {
    super(
      azureCredentials,
      async (token: ITokenPayload, done: VerifyCallback) => {
        if (Date.now() / 1000 > token.exp) {
          return done(new UnauthorizedException("access token is expired"))
        }

        const tokenUsername = token?.preferred_username?.slice(0, 9)
        const tokenAppId = !tokenUsername && token?.azp

        if (!tokenUsername && !tokenAppId) {
          return done(new UnauthorizedException("Missing User"))
        }

        let user: User

        if (tokenUsername) {
          try {
            user = await this.usersService.getUser(tokenUsername)
            if (!user) {
              return done(new UnauthorizedException("User is not recognized"))
            }
          } catch (err) {
            return done(err)
          }
        }
        return done(null, user, token)
      },
    )
  }
}

This works great. I need to have a backdoor logic for my application, so it bypasses the azureAD authorization if a certain header is sent in the request, lets say req.is_bypass

What's the best way to achive that? is it even possible or do i need to use a different logic for backdoor auth?

Thanks!

CodePudding user response:

In your LoginGuard's canActivate you have access to the ExecutionContext object, which can be used to get the current request. After your check of the isPublic metadata you can add the check you're looking to do

import { ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { AuthGuard } from "@nestjs/passport";

@Injectable()
export class LoginGuard extends AuthGuard(
    ["azure-ad"]
) {
    public constructor(private readonly reflector: Reflector) {
        super();
    }

    async canActivate(context: ExecutionContext) {
        const isPublic = this.reflector.get<boolean>(
            "isPublic",
            context.getHandler()
        );

        if (isPublic) {
            return true;
        }

        const req = context.switchToHttp().getRequest()
        if (req.is_bypass) {
          return true;
        }
        
        const result = (await super.canActivate(context)) as boolean;
        const request = context.switchToHttp().getRequest();
        await super.logIn(request);

        return result;
    }
}
  • Related