Skip to content

Commit

Permalink
Merge pull request AzureAD#1155 from AzureAD/authorization-code-flow-…
Browse files Browse the repository at this point in the history
…logger

Authorization Code Flow for Single Page Applications: Adding Logger class
  • Loading branch information
Prithvi Kanherkar committed Dec 19, 2019
2 parents 2a5a439 + ae2443a commit 1b2f684
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 17 deletions.
34 changes: 21 additions & 13 deletions src/app/config/ModuleConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,86 +6,93 @@ import { ICacheStorage } from "../../cache/ICacheStorage";
import { INetworkModule, NetworkRequestOptions } from "../../network/INetworkModule";
import { ICrypto, PkceCodes } from "../../crypto/ICrypto";
import { AuthError } from "../../error/AuthError";
import { ILoggerCallback, LogLevel } from "../../logger/Logger";

/**
* Use the configuration object to configure MSAL Modules and initialize the base interfaces for MSAL.
*
* This object allows you to configure important elements of MSAL functionality:
* - logger: logging for application
* - storage: this is where you configure storage implementation.
* - network: this is where you can configure network implementation.
* - crypto: implementation of crypto functions
*/
export type ModuleConfiguration = {
loggerOptions?: LoggerOptions,
storageInterface?: ICacheStorage,
networkInterface?: INetworkModule,
cryptoInterface?: ICrypto
};

/**
* Logger options to configure the logging that MSAL does.
*/
export type LoggerOptions = {
loggerCallbackInterface?: ILoggerCallback,
piiLoggingEnabled?: boolean
};

const DEFAULT_LOGGER_IMPLEMENTATION: LoggerOptions = {
loggerCallbackInterface: () => {
const notImplErr = "Logger - loggerCallbackInterface() has not been implemented.";
throw AuthError.createUnexpectedError(notImplErr);
},
piiLoggingEnabled: false
};

const DEFAULT_STORAGE_IMPLEMENTATION: ICacheStorage = {
clear: () => {
const notImplErr = "Storage interface - clear() has not been implemented for the cacheStorage interface.";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
containsKey: (key: string): boolean => {
const notImplErr = "Storage interface - containsKey() has not been implemented for the cacheStorage interface.";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
getItem: (key: string): string => {
const notImplErr = "Storage interface - getItem() has not been implemented for the cacheStorage interface.";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
getKeys: (): string[] => {
const notImplErr = "Storage interface - getKeys() has not been implemented for the cacheStorage interface.";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
removeItem: (key: string) => {
const notImplErr = "Storage interface - removeItem() has not been implemented for the cacheStorage interface.";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
setItem: (key: string, value: string) => {
const notImplErr = "Storage interface - setItem() has not been implemented for the cacheStorage interface.";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
}
};

const DEFAULT_NETWORK_IMPLEMENTATION: INetworkModule = {
async sendGetRequestAsync(url: string, options?: NetworkRequestOptions): Promise<any> {
const notImplErr = "Network interface - sendGetRequestAsync() has not been implemented";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
async sendPostRequestAsync(url: string, options?: NetworkRequestOptions): Promise<any> {
const notImplErr = "Network interface - sendPostRequestAsync() has not been implemented";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
}
};

const DEFAULT_CRYPTO_IMPLEMENTATION: ICrypto = {
createNewGuid: (): string => {
const notImplErr = "Crypto interface - createNewGuid() has not been implemented";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
base64Decode: (input: string): string => {
const notImplErr = "Crypto interface - base64Decode() has not been implemented";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
base64Encode: (input: string): string => {
const notImplErr = "Crypto interface - base64Encode() has not been implemented";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
},
async generatePkceCodes(): Promise<PkceCodes> {
const notImplErr = "Crypto interface - generatePkceCodes() has not been implemented";
console.warn(notImplErr);
throw AuthError.createUnexpectedError(notImplErr);
}
};
Expand All @@ -99,8 +106,9 @@ const DEFAULT_CRYPTO_IMPLEMENTATION: ICrypto = {
*
* @returns MsalConfiguration object
*/
export function buildModuleConfiguration({ storageInterface: storageImplementation, networkInterface: networkImplementation, cryptoInterface: cryptoImplementation }: ModuleConfiguration): ModuleConfiguration {
export function buildModuleConfiguration({ loggerOptions: userLoggerOption, storageInterface: storageImplementation, networkInterface: networkImplementation, cryptoInterface: cryptoImplementation }: ModuleConfiguration): ModuleConfiguration {
const overlayedConfig: ModuleConfiguration = {
loggerOptions: userLoggerOption || DEFAULT_LOGGER_IMPLEMENTATION,
storageInterface: storageImplementation || DEFAULT_STORAGE_IMPLEMENTATION,
networkInterface: networkImplementation || DEFAULT_NETWORK_IMPLEMENTATION,
cryptoInterface: cryptoImplementation || DEFAULT_CRYPTO_IMPLEMENTATION
Expand Down
4 changes: 2 additions & 2 deletions src/app/config/PublicClientSPAConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ const DEFAULT_AUTH_OPTIONS: AuthOptions = {
*
* @returns TConfiguration object
*/
export function buildPublicClientSPAConfiguration({ auth, storageInterface, networkInterface, cryptoInterface }: PublicClientSPAConfiguration): PublicClientSPAConfiguration {
const baseConfig = buildModuleConfiguration({storageInterface, networkInterface, cryptoInterface});
export function buildPublicClientSPAConfiguration({ auth, loggerOptions, storageInterface, networkInterface, cryptoInterface }: PublicClientSPAConfiguration): PublicClientSPAConfiguration {
const baseConfig = buildModuleConfiguration({loggerOptions, storageInterface, networkInterface, cryptoInterface});
const overlayedConfig: PublicClientSPAConfiguration = {
auth: { ...DEFAULT_AUTH_OPTIONS, ...auth },
...baseConfig
Expand Down
7 changes: 7 additions & 0 deletions src/app/module/AuthModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { StringUtils } from "../../utils/StringUtils";
import { IdToken } from "../../auth/IdToken";
import { buildClientInfo } from "../../auth/ClientInfo";
import { CacheHelpers } from "../../cache/CacheHelpers";
import { Logger } from "../../logger/Logger";

/**
* @hidden
Expand All @@ -40,6 +41,9 @@ export type ResponseStateInfo = {
*/
export abstract class AuthModule {

// Logger object
public logger: Logger;

// Application config
private config: ModuleConfiguration;

Expand All @@ -65,6 +69,9 @@ export abstract class AuthModule {
// Set the configuration
this.config = buildModuleConfiguration(configuration);

// Initialize the logger
this.logger = new Logger(configuration.loggerOptions.loggerCallbackInterface, configuration.loggerOptions.piiLoggingEnabled);

// Initialize crypto
this.cryptoObj = this.config.cryptoInterface;

Expand Down
1 change: 1 addition & 0 deletions src/app/module/AuthorizationCodeModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class AuthorizationCodeModule extends AuthModule {

constructor(configuration: PublicClientSPAConfiguration) {
super({
loggerOptions: configuration.loggerOptions,
storageInterface: configuration.storageInterface,
networkInterface: configuration.networkInterface,
cryptoInterface: configuration.cryptoInterface
Expand Down
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ export { TokenExchangeParameters } from "./request/TokenExchangeParameters";
export { AuthResponse, buildResponseStateOnly } from "./response/AuthResponse";
export { TokenResponse } from "./response/TokenResponse";
export { CodeResponse } from "./response/CodeResponse";
// Logger Callback
export { ILoggerCallback, LogLevel } from "./logger/Logger";
// Errors
export { AuthError, AuthErrorMessage } from "./error/AuthError";
export { ClientAuthError, ClientAuthErrorMessage } from "./error/ClientAuthError";
export { ClientConfigurationError, ClientConfigurationErrorMessage } from "./error/ClientConfigurationError";
// Constants
// Constants and Utils
export { Constants, TemporaryCacheKeys, PersistentCacheKeys, ErrorCacheKeys } from "./utils/Constants";
export { StringUtils } from "./utils/StringUtils";
166 changes: 166 additions & 0 deletions src/logger/Logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { StringUtils } from "../utils/StringUtils";
import pkg from "../../package.json";

export type LoggerMessageOptions = {
logLevel: LogLevel,
correlationId?: string,
containsPii?: boolean
};

export enum LogLevel {
Error,
Warning,
Info,
Verbose
};

export interface ILoggerCallback {
(level: LogLevel, message: string, containsPii: boolean): void;
}

export class Logger {

/**
* @hidden
*/
private correlationId: string;

/**
* @hidden
*/
private level: LogLevel = LogLevel.Info;

/**
* @hidden
*/
private piiLoggingEnabled: boolean;

/**
* @hidden
*/
private localCallback: ILoggerCallback;

constructor(localCallback: ILoggerCallback, piiLoggingEnabled: boolean) {
this.localCallback = localCallback;
this.piiLoggingEnabled = piiLoggingEnabled;
}

/**
* @hidden
*/
private logMessage(logMessage: string, options: LoggerMessageOptions): void {
if ((options.logLevel > this.level) || (!this.piiLoggingEnabled && options.containsPii)) {
return;
}
const timestamp = new Date().toUTCString();
let logHeader: string = StringUtils.isEmpty(this.correlationId) ? `[${timestamp}] : ` : `[${timestamp}] : [${this.correlationId}]`;
const log = `${logHeader} : ${pkg.version} : ${LogLevel[options.logLevel]} - ${logMessage}`;
this.executeCallback(options.logLevel, log, options.containsPii);
}

/**
* @hidden
*/
executeCallback(level: LogLevel, message: string, containsPii: boolean) {
if (this.localCallback) {
this.localCallback(level, message, containsPii);
}
}

/**
* @hidden
*/
error(message: string, correlationId?: string): void {
this.logMessage(message, {
logLevel: LogLevel.Error,
containsPii: false,
correlationId: correlationId || ""
});
}

/**
* @hidden
*/
errorPii(message: string, correlationId?: string): void {
this.logMessage(message, {
logLevel: LogLevel.Error,
containsPii: true,
correlationId: correlationId || ""
});
}

/**
* @hidden
*/
warning(message: string, correlationId?: string): void {
this.logMessage(message, {
logLevel: LogLevel.Warning,
containsPii: false,
correlationId: correlationId || ""
});
}

/**
* @hidden
*/
warningPii(message: string, correlationId?: string): void {
this.logMessage(message, {
logLevel: LogLevel.Warning,
containsPii: true,
correlationId: correlationId || ""
});
}

/**
* @hidden
*/
info(message: string, correlationId?: string): void {
this.logMessage(message, {
logLevel: LogLevel.Info,
containsPii: false,
correlationId: correlationId || ""
});
}

/**
* @hidden
*/
infoPii(message: string, correlationId?: string): void {
this.logMessage(message, {
logLevel: LogLevel.Info,
containsPii: true,
correlationId: correlationId || ""
});
}

/**
* @hidden
*/
verbose(message: string, correlationId?: string): void {
this.logMessage(message, {
logLevel: LogLevel.Verbose,
containsPii: false,
correlationId: correlationId || ""
});
}

/**
* @hidden
*/
verbosePii(message: string, correlationId?: string): void {
this.logMessage(message, {
logLevel: LogLevel.Verbose,
containsPii: true,
correlationId: correlationId || ""
});
}

isPiiLoggingEnabled(): boolean {
return this.piiLoggingEnabled;
}
}
1 change: 0 additions & 1 deletion src/server/CodeRequestParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ export class CodeRequestParameters {
const pkceCodes = await this.cryptoObj.generatePkceCodes();
str.push(`code_challenge=${encodeURIComponent(pkceCodes.challenge)}`);
str.push("code_challenge_method=S256");
console.log(`PKCE Codes: ${JSON.stringify(pkceCodes)}`);

if (this.userRequest.prompt) {
str.push("prompt=" + encodeURIComponent(this.userRequest.prompt));
Expand Down

0 comments on commit 1b2f684

Please sign in to comment.