Adding user module and removing msgraph module
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { CanActivate } from '@nestjs/common';
|
||||
import { ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { AuthGuard as PassportAuthGuard } from '@nestjs/passport';
|
||||
@@ -6,21 +5,18 @@ import { Observable } from 'rxjs';
|
||||
import { IS_PUBLIC_KEY } from './public.decorator';
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuard extends PassportAuthGuard('azure-ad') implements CanActivate {
|
||||
export class AuthGuard extends PassportAuthGuard('azure-ad') {
|
||||
constructor(private readonly reflector: Reflector) {
|
||||
super();
|
||||
}
|
||||
|
||||
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
|
||||
const isPublic = this.reflector.get<boolean>(
|
||||
IS_PUBLIC_KEY,
|
||||
context.getHandler()
|
||||
);
|
||||
const req = context.switchToHttp().getRequest();
|
||||
const authHeader = req.headers.authorization;
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass()
|
||||
])
|
||||
|
||||
if (isPublic && !token) {
|
||||
if (isPublic) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import { ConfigurableModuleClass } from './auth.module-definition';
|
||||
defaultStrategy: 'azure-ad'
|
||||
})
|
||||
],
|
||||
providers: [AzureAdStrategy]
|
||||
providers: [
|
||||
AzureAdStrategy
|
||||
]
|
||||
})
|
||||
export class AuthModule extends ConfigurableModuleClass {}
|
||||
|
||||
@@ -3,8 +3,6 @@ import { PassportStrategy } from "@nestjs/passport";
|
||||
import { AuthModuleOptions } from './auth.interface'
|
||||
import { MODULE_OPTIONS_TOKEN } from "./auth.module-definition";
|
||||
import { BearerStrategy } from 'passport-azure-ad'
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import * as jwksRsa from 'jwks-rsa';
|
||||
|
||||
@Injectable()
|
||||
export class AzureAdStrategy extends PassportStrategy(
|
||||
@@ -25,27 +23,4 @@ export class AzureAdStrategy extends PassportStrategy(
|
||||
async validate(data: any): Promise<any> {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
// @Injectable()
|
||||
// export class AzureAdStrategy extends PassportStrategy(Strategy, 'azure-ad') {
|
||||
// constructor(@Inject(MODULE_OPTIONS_TOKEN) authModuleOptions: AuthModuleOptions) {
|
||||
// super({
|
||||
// jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
// audience: `api://${authModuleOptions.clientId}`,
|
||||
// issuer: `https://sts.windows.net/${authModuleOptions.tenantId}`,
|
||||
// algorithms: ['RS256'],
|
||||
// ignoreExpiration: true,
|
||||
// secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
||||
// cache: true,
|
||||
// rateLimit: true,
|
||||
// jwksRequestsPerMinute: 5,
|
||||
// jwksUri: `https://login.microsoftonline.com/${authModuleOptions.tenantId}`
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
|
||||
// validate(payload: any) {
|
||||
// return payload;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// import { Logger } from '@nestjs/common';
|
||||
// import { LoggerService } from '@dapr/dapr';
|
||||
|
||||
// export class DaprLogger implements LoggerService {
|
||||
// private readonly logger = new Logger(DaprLogger.name);
|
||||
|
||||
// constructor() {}
|
||||
|
||||
// error(message: any, ...optionalParams: any[]): void {
|
||||
// this.logger.error(message, ...optionalParams);
|
||||
// }
|
||||
|
||||
// warn(message: any, ...optionalParams: any[]): void {
|
||||
// this.logger.warn(message, ...optionalParams);
|
||||
// }
|
||||
|
||||
// info(message: any, ...optionalParams: any[]): void {
|
||||
// this.logger.log(message, ...optionalParams);
|
||||
// }
|
||||
|
||||
// verbose(message: any, ...optionalParams: any[]): void {
|
||||
// this.logger.verbose(message, ...optionalParams);
|
||||
// }
|
||||
|
||||
// debug(message: any, ...optionalParams: any[]): void {
|
||||
// this.logger.debug(message, ...optionalParams);
|
||||
// }
|
||||
// }
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { DaprService } from "./dapr.service";
|
||||
|
||||
@Module({
|
||||
providers: [ DaprService ],
|
||||
exports: [ DaprService ]
|
||||
})
|
||||
export class DaprModule {}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DaprClient } from '@dapr/dapr';
|
||||
|
||||
@Injectable()
|
||||
export class DaprService {
|
||||
daprClient: DaprClient;
|
||||
private readonly logger = new Logger(DaprService.name);
|
||||
|
||||
constructor() {
|
||||
this.logger.log('Initializing DaprClient')
|
||||
this.daprClient = new DaprClient();
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,11 @@ export * from './auth/auth.module';
|
||||
export * from './auth/auth.gaurd';
|
||||
export * from './auth/public.decorator';
|
||||
|
||||
// Dapr
|
||||
|
||||
export * from './dapr/dapr.module'
|
||||
export * from './dapr/dapr.service'
|
||||
|
||||
// MS Graph
|
||||
|
||||
export * from './msGraph/ms-graph.module';
|
||||
export * from './msGraph/ms-graph.service';
|
||||
export { Client as MsGraphClient } from '@microsoft/microsoft-graph-client';
|
||||
|
||||
// Custom Error
|
||||
|
||||
export * from './util/customError';
|
||||
|
||||
// User
|
||||
|
||||
export * from './user/user.module';
|
||||
export * from './user/user.service';
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export interface MsGraphModuleOptions {
|
||||
tenantId: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import { ConfigurableModuleBuilder } from '@nestjs/common';
|
||||
import { MsGraphModuleOptions } from './ms-graph.interface';
|
||||
|
||||
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = new ConfigurableModuleBuilder<MsGraphModuleOptions>().build()
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MsGraphService } from './ms-graph.service';
|
||||
import { ConfigurableModuleClass } from './ms-graph.module-definition';
|
||||
|
||||
@Module({
|
||||
providers: [MsGraphService],
|
||||
exports: [MsGraphService]
|
||||
})
|
||||
export class MsGraphModule extends ConfigurableModuleClass {}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { MsGraphModuleOptions } from './ms-graph.interface';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { AuthenticationResult, ConfidentialClientApplication, OnBehalfOfRequest } from '@azure/msal-node';
|
||||
import { MODULE_OPTIONS_TOKEN } from './ms-graph.module-definition';
|
||||
|
||||
@Injectable()
|
||||
export class MsGraphService {
|
||||
constructor(@Inject(MODULE_OPTIONS_TOKEN) private msGraphModuleOptions: MsGraphModuleOptions) {}
|
||||
|
||||
async getMsGraphAuth(accessToken: string, scopes: string[]): Promise<string> {
|
||||
try {
|
||||
const oboRequest: OnBehalfOfRequest = {
|
||||
oboAssertion: accessToken,
|
||||
scopes: scopes
|
||||
}
|
||||
const cca = new ConfidentialClientApplication({
|
||||
auth: {
|
||||
clientId: this.msGraphModuleOptions.clientId,
|
||||
clientSecret: this.msGraphModuleOptions.clientSecret,
|
||||
authority: `https://login.microsoftonline.com/${this.msGraphModuleOptions.tenantId}`
|
||||
}
|
||||
});
|
||||
const authenticationResult: AuthenticationResult = await cca.acquireTokenOnBehalfOf(oboRequest);
|
||||
|
||||
return authenticationResult.accessToken
|
||||
} catch (error) {
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
async getMsGraphClientDelegated(accessToken): Promise<Client> {
|
||||
const client = await Client.init({
|
||||
authProvider: (done) => {
|
||||
done(null, accessToken);
|
||||
}
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
1
libs/modules/src/user/user.constants.ts
Normal file
1
libs/modules/src/user/user.constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const USER_MODULE_OPTIONS = 'USER_MODULE_OPTIONS'
|
||||
71
libs/modules/src/user/user.controller.ts
Normal file
71
libs/modules/src/user/user.controller.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Headers,
|
||||
Query,
|
||||
StreamableFile,
|
||||
} from '@nestjs/common';
|
||||
import { UserService } from './user.service';
|
||||
import { Client as MsGraphClient } from '@microsoft/microsoft-graph-client';
|
||||
|
||||
@Controller('user')
|
||||
export class UserController {
|
||||
constructor(
|
||||
private readonly userService: UserService
|
||||
) {}
|
||||
|
||||
@Get('photo')
|
||||
async getProfilePhoto(@Headers() headers: any): Promise<StreamableFile> {
|
||||
try {
|
||||
const graphToken: string = await this.userService.getMsGraphAuth(
|
||||
headers.authorization.replace('Bearer ', ''),
|
||||
['user.read']
|
||||
);
|
||||
const client: MsGraphClient =
|
||||
await this.userService.getMsGraphClientDelegated(graphToken);
|
||||
const blob: Blob = await client.api(`me/photos('48x48')/$value`).get();
|
||||
const arrayBuffer: ArrayBuffer = await blob.arrayBuffer();
|
||||
const buffer: Buffer = Buffer.from(arrayBuffer);
|
||||
|
||||
return new StreamableFile(buffer, {
|
||||
type: 'application/json',
|
||||
disposition: `attachment; filename="user_photo.png"`
|
||||
});
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
@Get('profile')
|
||||
async getUserProfile(@Headers() headers: any): Promise<any> {
|
||||
try {
|
||||
const graphToken: string = await this.userService.getMsGraphAuth(
|
||||
headers.authorization.replace('Bearer ', ''),
|
||||
['user.read']
|
||||
);
|
||||
const client: MsGraphClient =
|
||||
await this.userService.getMsGraphClientDelegated(graphToken);
|
||||
const userProfile = await client.api(`me`).get();
|
||||
|
||||
return userProfile;
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
@Get('search')
|
||||
async searchUsers(
|
||||
@Headers() headers: any,
|
||||
@Query('search') search: any
|
||||
): Promise<any> {
|
||||
try {
|
||||
const accessToken: string = headers.authorization.replace('Bearer ', '');
|
||||
const personSearchResults: any[] =
|
||||
await this.userService.searchUsers(accessToken, search);
|
||||
|
||||
return personSearchResults;
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
5
libs/modules/src/user/user.interface.ts
Normal file
5
libs/modules/src/user/user.interface.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface UserModuleOptions {
|
||||
tenantId: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
}
|
||||
4
libs/modules/src/user/user.module-definition.ts
Normal file
4
libs/modules/src/user/user.module-definition.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { ConfigurableModuleBuilder } from '@nestjs/common';
|
||||
import { UserModuleOptions } from './user.interface';
|
||||
|
||||
export const { ConfigurableModuleClass, ASYNC_OPTIONS_TYPE, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } = new ConfigurableModuleBuilder<UserModuleOptions>().build()
|
||||
11
libs/modules/src/user/user.module.ts
Normal file
11
libs/modules/src/user/user.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UserService } from './user.service';
|
||||
import { ConfigurableModuleClass } from './user.module-definition';
|
||||
import { UserController } from './user.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [UserController],
|
||||
providers: [UserService],
|
||||
exports: [UserService]
|
||||
})
|
||||
export class UserModule extends ConfigurableModuleClass {}
|
||||
96
libs/modules/src/user/user.service.ts
Normal file
96
libs/modules/src/user/user.service.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { Inject, Injectable, StreamableFile } from '@nestjs/common';
|
||||
import { UserModuleOptions } from './user.interface';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { AuthenticationResult, ConfidentialClientApplication, OnBehalfOfRequest } from '@azure/msal-node';
|
||||
import { MODULE_OPTIONS_TOKEN } from './user.module-definition';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(@Inject(MODULE_OPTIONS_TOKEN) private userModuleOptions: UserModuleOptions) {}
|
||||
|
||||
async getMsGraphAuth(accessToken: string, scopes: string[]): Promise<string> {
|
||||
try {
|
||||
const oboRequest: OnBehalfOfRequest = {
|
||||
oboAssertion: accessToken,
|
||||
scopes: scopes
|
||||
}
|
||||
const cca = new ConfidentialClientApplication({
|
||||
auth: {
|
||||
clientId: this.userModuleOptions.clientId,
|
||||
clientSecret: this.userModuleOptions.clientSecret,
|
||||
authority: `https://login.microsoftonline.com/${this.userModuleOptions.tenantId}`
|
||||
}
|
||||
});
|
||||
const authenticationResult: AuthenticationResult = await cca.acquireTokenOnBehalfOf(oboRequest);
|
||||
|
||||
return authenticationResult.accessToken
|
||||
} catch (error) {
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
async getMsGraphClientDelegated(accessToken): Promise<Client> {
|
||||
const client = await Client.init({
|
||||
authProvider: (done) => {
|
||||
done(null, accessToken);
|
||||
}
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
async getProfilePhoto(accessToken: string, scopes: string[]): Promise<Buffer> {
|
||||
try {
|
||||
const graphToken: string = await this.getMsGraphAuth(accessToken, scopes);
|
||||
const client: Client = await this.getMsGraphClientDelegated(graphToken);
|
||||
const blob: Blob = await client.api(`me/photos('48x48)/$value`).get();
|
||||
const arrayBuffer: ArrayBuffer = await blob.arrayBuffer();
|
||||
const buffer: Buffer = Buffer.from(arrayBuffer);
|
||||
|
||||
return buffer;
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
async getUserProfile(accessToken): Promise<any> {
|
||||
try {
|
||||
const graphToken: string = await this.getMsGraphAuth(accessToken, ['user.read']);
|
||||
const client: Client = await this.getMsGraphClientDelegated(graphToken);
|
||||
const userProfile = await client.api(`me`).get();
|
||||
|
||||
return userProfile
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
async searchUsers(accessToken, searchText: string): Promise<any> {
|
||||
try {
|
||||
const graphToken: string = await this.getMsGraphAuth(accessToken, ['user.read']);
|
||||
const client: Client = await this.getMsGraphClientDelegated(graphToken);
|
||||
const results: any = await client
|
||||
.api('users')
|
||||
.header('ConsistencyLevel', 'eventual')
|
||||
.search(`"displayName:${searchText}"`)
|
||||
.orderby('displayName')
|
||||
.select(['displayName', 'userPrincipalName'])
|
||||
.get();
|
||||
let personResults: any[];
|
||||
|
||||
if (results.value) {
|
||||
personResults = results.value.filter((result: any) => {
|
||||
if (result.userPrincipalName !== null) {
|
||||
return result;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
personResults = []
|
||||
}
|
||||
|
||||
return personResults;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user