import { CanActivate, ExecutionContext, Inject, Injectable, UnauthorizedException, } from '@nestjs/common'; import * as KeycloakConnect from 'keycloak-connect'; import { KEYCLOAK_INSTANCE } from '../constants'; import { Reflector } from '@nestjs/core'; import { KeycloakedRequest } from '../keycloaked-request'; interface NewTokenInterface extends KeycloakConnect.Token { authorities: Array; } declare module 'keycloak-connect' { interface GrantType { access_token?: NewTokenInterface refresh_token?: string id_token?: string expires_in?: string token_type?: string } } /** * An authentication guard. Will return a 401 unauthorized when it is unable to * verify the JWT token or Bearer header is missing. */ @Injectable() export class AuthGuard implements CanActivate { constructor( @Inject(KEYCLOAK_INSTANCE) private keycloak: KeycloakConnect.Keycloak, private reflector: Reflector ) { } async canActivate(context: ExecutionContext): Promise { const request: KeycloakedRequest = context.switchToHttp().getRequest(); const hasToken = !!this.reflector.get("has-token", context.getHandler()); const roles = this.reflector.get<(string | string[])[]>("roles", context.getHandler()); const authorities = this.reflector.get<(string | string[])[]>("authorities", context.getHandler()); const jwt = this.extractJwt(request.headers); let grant: KeycloakConnect.Grant | undefined; let user; if (!hasToken) { return true; } if (jwt) { grant = await this.keycloak.grantManager.createGrant({ "access_token": jwt }); } else { throw new UnauthorizedException(); } if(grant){ request.grant = (grant as any) as KeycloakConnect.GrantType; if (!grant.isExpired()) { user = grant.access_token && await this.keycloak.grantManager.userInfo(grant.access_token); } else { throw new UnauthorizedException(); } request.user = user; if (authorities && request.grant) { return authorities.some(authority => Array.isArray(authority) ? authority.every(innerAuthority => request.grant?.access_token?.authorities.includes(innerAuthority)) : request.grant?.access_token?.authorities.includes(authority)); } if(roles && request.grant){ return roles.some(role => Array.isArray(role) ? role.every(innerRole => request.grant?.access_token?.hasRole(innerRole)) : request.grant?.access_token?.hasRole(role)); } return true; } return false; } extractJwt(headers: Headers) { const auth = headers['authorization']?.split(' '); if (auth && auth[0] && auth[0].toLowerCase() === 'bearer') { return auth[1] } return null; } }