adding ms grpah client

This commit is contained in:
2024-06-05 21:28:12 -05:00
parent 373337d8d6
commit 1de80a7a20
19 changed files with 858 additions and 101 deletions

View File

@@ -1,3 +0,0 @@
export interface AppConfigOptions {
url: string;
}

View File

@@ -1,31 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import { AppConfigOptions } from './app-config.interface';
import { APP_CONFIG_OPTIONS } from './app-config.constants';
import axios, { AxiosResponse } from 'axios';
import { FeatureFlagValue } from '@azure/app-configuration';
@Injectable()
export class AppConfigService {
constructor(
@Inject(APP_CONFIG_OPTIONS)
private appConfigOptions: AppConfigOptions
) { }
async getFeatureFlags(token: string, key: string, label: string): Promise<FeatureFlagValue[]> {
try {
const response: AxiosResponse = await axios.get(
`${this.appConfigOptions.url}/kv?key=.appconfig.featureflag/${key}&label=${label}`,
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
const featureFlags: FeatureFlagValue[] = response.data.items.map((item) => item.value)
return featureFlags;
} catch (error) {
return error;
}
}
}

View File

@@ -0,0 +1,25 @@
import { CanActivate } from '@nestjs/common';
import { ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard as PassportAuthGuard } from '@nestjs/passport';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard extends PassportAuthGuard('azure-ad') implements CanActivate {
constructor(private readonly reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const isPublic = this.reflector.get<boolean>(
'isPublic',
context.getHandler()
);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}

View File

@@ -1,3 +1,5 @@
export interface AuthOptions {
tenantId: string;
clientId: string;
clientSecret: string;
}

View File

@@ -1,21 +1,26 @@
import { Module, DynamicModule } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthOptions } from './auth.interface';
import { AUTH_OPTIONS } from './auth.constants';
import { PassportModule } from '@nestjs/passport';
import { AzureAdStrategy } from './auth.strategy';
@Module({})
export class AuthModule {
static register(options: AuthOptions): DynamicModule {
return {
module: AuthModule,
imports: [
PassportModule.register({
defaultStrategy: 'azure-ad'
})
],
providers: [
{
provide: AUTH_OPTIONS,
useValue: options
},
AuthService
],
exports: [AuthService]
AzureAdStrategy
]
};
}
}

View File

@@ -1,41 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import { AuthOptions } from './auth.interface';
import { AUTH_OPTIONS } from './auth.constants';
import axios, { AxiosResponse } from 'axios';
@Injectable()
export class AuthService {
constructor(
@Inject(AUTH_OPTIONS)
private authOptions: AuthOptions
) { }
async getToken(
grantType: string,
clientId: string,
clientSecret: string,
resource: string
): Promise<string> {
try {
const { data }: AxiosResponse = await axios.post(
`https://login.microsoftonline.com/${this.authOptions.tenantId}/oauth2/token`,
{
grant_type: grantType,
client_id: clientId,
client_secret: clientSecret,
resource: resource
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
return data.access_token;
} catch (error) {
return error;
}
}
}

View File

@@ -0,0 +1,24 @@
import { Inject, Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { BearerStrategy } from 'passport-azure-ad';
import { AuthOptions } from './auth.interface'
import { AUTH_OPTIONS } from "./auth.constants";
@Injectable()
export class AzureAdStrategy extends PassportStrategy(
BearerStrategy,
'azure-ad'
) {
constructor(@Inject(AUTH_OPTIONS) authOptions: AuthOptions) {
super({
identityMetadata: `https://login.microsoftonline.com/${authOptions.tenantId}/v2.0/.well-known/openid-configuration`,
clientID: authOptions.clientId,
loggingLevel: 'info',
loggingNoPII: false
})
}
async validate(data: any): Promise<any> {
return data;
}
}

View File

@@ -0,0 +1,3 @@
import { SetMetadata } from "@nestjs/common";
export const Public = () => SetMetadata('isPublic', true);

View File

@@ -0,0 +1,6 @@
export interface AppConfigOptions {
url: string;
tenantId: string;
clientId: string;
clientSecret: string;
}

View File

@@ -1,7 +1,7 @@
import { Module, DynamicModule } from '@nestjs/common';
import { AppConfigService } from './app-config.service';
import { AppConfigOptions } from './app-config.interface';
import { APP_CONFIG_OPTIONS } from './app-config.constants';
import { AppConfigService } from './az-app-config.service';
import { AppConfigOptions } from './az-app-config.interface';
import { APP_CONFIG_OPTIONS } from './az-app-config.constants';
@Module({})
export class AppConfigModule {

View File

@@ -0,0 +1,55 @@
import { Inject, Injectable } from '@nestjs/common';
import { AppConfigOptions } from './az-app-config.interface';
import { APP_CONFIG_OPTIONS } from './az-app-config.constants';
import axios, { AxiosResponse } from 'axios';
import { FeatureFlagValue } from '@azure/app-configuration';
@Injectable()
export class AppConfigService {
constructor(
@Inject(APP_CONFIG_OPTIONS)
private appConfigOptions: AppConfigOptions
) { }
async getToken(): Promise<string> {
try {
const { data }: AxiosResponse = await axios.post(
`https://login.microsoftonline.com/${this.appConfigOptions.tenantId}/oauth2/token`,
{
grant_type: 'client_credentials',
client_id: this.appConfigOptions.clientId,
client_secret: this.appConfigOptions.clientSecret,
resource: 'https://azconfig.io'
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
return data.access_token;
} catch (error) {
return error;
}
}
async getFeatureFlags(token: string, key: string, label: string): Promise<FeatureFlagValue[]> {
try {
const token: string = await this.getToken();
const response: AxiosResponse = await axios.get(
`${this.appConfigOptions.url}/kv?key=.appconfig.featureflag/${key}&label=${label}`,
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
const featureFlags: FeatureFlagValue[] = response.data.items.map((item) => JSON.parse(item.value));
return featureFlags;
} catch (error) {
return error;
}
}
}

View File

@@ -1,4 +1,16 @@
export * from './appConfig/app-config.module';
export * from './appConfig/app-config.service';
// Azure App Configuration
export * from './azAppConfig/az-app-config.module';
export * from './azAppConfig/az-app-config.service';
// Auth
export * from './auth/auth.module';
export * from './auth/auth.service'
export * from './auth/auth.gaurd';
export * from './auth/public.decorator';
// MS Graph
export * from './msGraph/ms-graph.module';
export * from './msGraph/ms-graph.service';
export { Client as MsGraphClient } from '@microsoft/microsoft-graph-client';

View File

@@ -0,0 +1 @@
export const MS_GRAPH_OPTIONS = 'MS_GRAPH_OPTIONS';

View File

@@ -0,0 +1,5 @@
export interface MsGraphOptions {
tenantId: string;
clientId: string;
clientSecret: string;
}

View File

@@ -0,0 +1,21 @@
import { Module, DynamicModule } from '@nestjs/common';
import { MsGraphService } from './ms-graph.service';
import { MsGraphOptions } from './ms-graph.interface';
import { MS_GRAPH_OPTIONS } from './ms-graph.constants';
@Module({})
export class MsGraphModule {
static register(options: MsGraphOptions): DynamicModule {
return {
module: MsGraphModule,
providers: [
{
provide: MS_GRAPH_OPTIONS,
useValue: options
},
MsGraphService
],
exports: [MsGraphService]
}
}
}

View File

@@ -0,0 +1,22 @@
import { Inject, Injectable } from '@nestjs/common';
import { MsGraphOptions } from './ms-graph.interface';
import { MS_GRAPH_OPTIONS } from './ms-graph.constants';
import { ClientSecretCredential } from '@azure/identity';
import { Client } from '@microsoft/microsoft-graph-client';
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials'
@Injectable()
export class MsGraphService {
constructor(@Inject(MS_GRAPH_OPTIONS) private msGraphOptions: MsGraphOptions) {}
async getMsGraphClient(): Promise<Client> {
const credential = new ClientSecretCredential(this.msGraphOptions.tenantId, this.msGraphOptions.clientId, this.msGraphOptions.clientSecret);
const authProvider = new TokenCredentialAuthenticationProvider(credential, { scopes: ['.default'] });
const client = Client.initWithMiddleware({
debugLogging: true,
authProvider
});
return client;
}
}