Skip to content

Commit

Permalink
Merge pull request #2023 from AzureAD/msal-node/confidential-client
Browse files Browse the repository at this point in the history
[msal node] Adds confidential client
  • Loading branch information
sangonzal committed Aug 4, 2020
2 parents 48face9 + e4d0de5 commit cb1bcbe
Show file tree
Hide file tree
Showing 23 changed files with 560 additions and 59 deletions.
16 changes: 13 additions & 3 deletions lib/msal-common/src/client/AuthorizationCodeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ export class AuthorizationCodeClient extends BaseClient {
}

// Get postLogoutRedirectUri.
const postLogoutUriParam = logoutRequest.postLogoutRedirectUri ?
const postLogoutUriParam = logoutRequest.postLogoutRedirectUri ?
`?${AADServerParamKeys.POST_LOGOUT_URI}=${encodeURIComponent(logoutRequest.postLogoutRedirectUri)}` : "";

const correlationIdParam = logoutRequest.correlationId ?
const correlationIdParam = logoutRequest.correlationId ?
`&${AADServerParamKeys.CLIENT_REQUEST_ID}=${encodeURIComponent(logoutRequest.correlationId)}` : "";

// Construct logout URI.
const logoutUri = `${this.authority.endSessionEndpoint}${postLogoutUriParam}${correlationIdParam}`;
return logoutUri;
Expand Down Expand Up @@ -160,6 +160,16 @@ export class AuthorizationCodeClient extends BaseClient {
parameterBuilder.addCodeVerifier(request.codeVerifier);
}

if (this.config.clientCredentials.clientSecret) {
parameterBuilder.addClientSecret(this.config.clientCredentials.clientSecret);
}

if (this.config.clientCredentials.clientAssertion) {
const clientAssertion = this.config.clientCredentials.clientAssertion;
parameterBuilder.addClientAssertion(clientAssertion.assertion);
parameterBuilder.addClientAssertionType(clientAssertion.assertionType);
}

parameterBuilder.addGrantType(GrantType.AUTHORIZATION_CODE_GRANT);
parameterBuilder.addClientInfo();

Expand Down
10 changes: 10 additions & 0 deletions lib/msal-common/src/client/RefreshTokenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ export class RefreshTokenClient extends BaseClient {

parameterBuilder.addRefreshToken(request.refreshToken);

if (this.config.clientCredentials.clientSecret) {
parameterBuilder.addClientSecret(this.config.clientCredentials.clientSecret);
}

if (this.config.clientCredentials.clientAssertion) {
const clientAssertion = this.config.clientCredentials.clientAssertion;
parameterBuilder.addClientAssertion(clientAssertion.assertion);
parameterBuilder.addClientAssertionType(clientAssertion.assertionType);
}

return parameterBuilder.createQueryString();
}
}
24 changes: 22 additions & 2 deletions lib/msal-common/src/config/ClientConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const DEFAULT_TOKEN_RENEWAL_OFFSET_SEC = 300;
* - networkInterface - Network implementation
* - storageInterface - Storage implementation
* - systemOptions - Additional library options
* - clientCredentials - Credentials options for confidential clients
*/
export type ClientConfiguration = {
authOptions: AuthOptions,
Expand All @@ -35,7 +36,8 @@ export type ClientConfiguration = {
storageInterface?: CacheManager,
networkInterface?: INetworkModule,
cryptoInterface?: ICrypto,
libraryInfo?: LibraryInfo,
clientCredentials?: ClientCredentials,
libraryInfo?: LibraryInfo
serverTelemetryManager?: ServerTelemetryManager
};

Expand Down Expand Up @@ -65,7 +67,7 @@ export type SystemOptions = {

/**
* Use this to configure the logging that MSAL does, by configuring logger options in the Configuration object
*
*
* - loggerCallback - Callback for logger
* - piiLoggingEnabled - Sets whether pii logging is enabled
* - logLevel - Sets the level at which logging happens
Expand All @@ -86,6 +88,17 @@ export type LibraryInfo = {
os: string
};

/**
* Credentials for confidential clients
*/
export type ClientCredentials = {
clientSecret?: string,
clientAssertion? : {
assertion: string,
assertionType: string
};
};

const DEFAULT_AUTH_OPTIONS: AuthOptions = {
clientId: "",
authority: null,
Expand Down Expand Up @@ -142,6 +155,11 @@ const DEFAULT_LIBRARY_INFO: LibraryInfo = {
os: ""
};

const DEFAULT_CLIENT_CREDENTIALS: ClientCredentials = {
clientSecret: "",
clientAssertion: null
};

/**
* Function that sets the default options when not explicitly configured from app developer
*
Expand All @@ -157,6 +175,7 @@ export function buildClientConfiguration(
storageInterface: storageImplementation,
networkInterface: networkImplementation,
cryptoInterface: cryptoImplementation,
clientCredentials: clientCredentials,
libraryInfo: libraryInfo,
serverTelemetryManager: serverTelemetryManager
} : ClientConfiguration): ClientConfiguration {
Expand All @@ -167,6 +186,7 @@ export function buildClientConfiguration(
storageInterface: storageImplementation || new DefaultStorageClass(),
networkInterface: networkImplementation || DEFAULT_NETWORK_IMPLEMENTATION,
cryptoInterface: cryptoImplementation || DEFAULT_CRYPTO_IMPLEMENTATION,
clientCredentials: clientCredentials || DEFAULT_CLIENT_CREDENTIALS,
libraryInfo: { ...DEFAULT_LIBRARY_INFO, ...libraryInfo },
serverTelemetryManager: serverTelemetryManager || null
};
Expand Down
22 changes: 22 additions & 0 deletions lib/msal-common/src/error/ClientAuthError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@ export const ClientAuthErrorMessage = {
unexpectedCredentialType: {
code: "unexpected_credential_type",
desc: "Unexpected credential type."
},
invalidAssertion: {
code: "invalid_assertion",
desc: "Client assertion must meet requirements described in https://tools.ietf.org/html/rfc7515"
},
invalidClientCredential: {
code: "invalid_client_credential",
desc: "Client credential (secret, certificate, or assertion) must not be empty when creating a confidential client. An application should at most have one credential"
}
};

Expand Down Expand Up @@ -422,4 +430,18 @@ export class ClientAuthError extends AuthError {
static createUnexpectedCredentialTypeError(): ClientAuthError {
return new ClientAuthError(ClientAuthErrorMessage.unexpectedCredentialType.code, `${ClientAuthErrorMessage.unexpectedCredentialType.desc}`);
}

/**
* Throws error if client assertion is not valid.
*/
static createInvalidAssertionError(): ClientAuthError {
return new ClientAuthError(ClientAuthErrorMessage.invalidAssertion.code, `${ClientAuthErrorMessage.invalidAssertion.desc}`);
}

/**
* Throws error if client assertion is not valid.
*/
static createInvalidCredentialError(): ClientAuthError {
return new ClientAuthError(ClientAuthErrorMessage.invalidClientCredential.code, `${ClientAuthErrorMessage.invalidClientCredential.desc}`);
}
}
25 changes: 20 additions & 5 deletions lib/msal-common/src/request/RequestParameterBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class RequestParameterBuilder {

private parameters: Map<string, string>;

constructor(){
constructor() {
this.parameters = new Map<string, string>();
}

Expand Down Expand Up @@ -198,10 +198,25 @@ export class RequestParameterBuilder {
* add client_secret
* @param clientSecret
*/
// TODO uncomment when confidential client flow is added.
// addClientSecret(clientSecret: string): void {
// params.set(`${AADServerParamKeys.CLIENT_SECRET}`, clientSecret);
// }
addClientSecret(clientSecret: string): void {
this.parameters.set(AADServerParamKeys.CLIENT_SECRET, encodeURIComponent(clientSecret));
}

/**
* add clientAssertion for confidential client flows
* @param clientAssertion
*/
addClientAssertion(clientAssertion: string): void {
this.parameters.set(AADServerParamKeys.CLIENT_ASSERTION, encodeURIComponent(clientAssertion));
}

/**
* add clientAssertionType for confidential client flows
* @param clientAssertionType
*/
addClientAssertionType(clientAssertionType: string): void {
this.parameters.set(AADServerParamKeys.CLIENT_ASSERTION_TYPE, encodeURIComponent(clientAssertionType));
}

/**
* add grant type
Expand Down
5 changes: 4 additions & 1 deletion lib/msal-common/src/utils/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ export enum AADServerParamKeys {
X_CLIENT_OS = "x-client-OS",
X_CLIENT_CPU = "x-client-CPU",
POST_LOGOUT_URI = "post_logout_redirect_uri",
DEVICE_CODE = "device_code"
DEVICE_CODE = "device_code",
CLIENT_SECRET = "client_secret",
CLIENT_ASSERTION = "client_assertion",
CLIENT_ASSERTION_TYPE = "client_assertion_type",
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ describe("AuthorizationCodeClient unit tests", () => {
expect(createTokenRequestBodySpy.returnValues[0]).to.contain(`${AADServerParamKeys.CODE}=${TEST_TOKENS.AUTHORIZATION_CODE}`);
expect(createTokenRequestBodySpy.returnValues[0]).to.contain(`${AADServerParamKeys.GRANT_TYPE}=${Constants.CODE_GRANT_TYPE}`);
expect(createTokenRequestBodySpy.returnValues[0]).to.contain(`${AADServerParamKeys.CODE_VERIFIER}=${TEST_CONFIG.TEST_VERIFIER}`);
expect(createTokenRequestBodySpy.returnValues[0]).to.contain(`${AADServerParamKeys.CLIENT_SECRET}=${TEST_CONFIG.MSAL_CLIENT_SECRET}`);
});
});

Expand Down
3 changes: 3 additions & 0 deletions lib/msal-common/test/client/ClientTestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ export class ClientTestUtils {
loggerOptions: {
loggerCallback: testLoggerCallback,
},
clientCredentials: {
clientSecret: TEST_CONFIG.MSAL_CLIENT_SECRET,
},
libraryInfo: {
sku: Constants.SKU,
version: TEST_CONFIG.TEST_VERSION,
Expand Down
1 change: 1 addition & 0 deletions lib/msal-common/test/client/RefreshTokenClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,6 @@ describe("RefreshTokenClient unit tests", () => {
expect(createTokenRequestBodySpy.returnValues[0]).to.contain(`${AADServerParamKeys.CLIENT_ID}=${TEST_CONFIG.MSAL_CLIENT_ID}`);
expect(createTokenRequestBodySpy.returnValues[0]).to.contain(`${AADServerParamKeys.REFRESH_TOKEN}=${TEST_TOKENS.REFRESH_TOKEN}`);
expect(createTokenRequestBodySpy.returnValues[0]).to.contain(`${AADServerParamKeys.GRANT_TYPE}=${GrantType.REFRESH_TOKEN_GRANT}`);
expect(createTokenRequestBodySpy.returnValues[0]).to.contain(`${AADServerParamKeys.CLIENT_SECRET}=${TEST_CONFIG.MSAL_CLIENT_SECRET}`);
});
});
1 change: 1 addition & 0 deletions lib/msal-common/test/utils/StringConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export const TEST_CONFIG = {
TEST_VERSION: "1.0.0",
TEST_OS: "win32",
TEST_CPU: "x86",
TEST_ASSERTION_TYPE: "jwt_bearer"
};

export const RANDOM_TEST_GUID = "11553a9b-7116-48b1-9d48-f6d4a8ff8371";
Expand Down
Loading

0 comments on commit cb1bcbe

Please sign in to comment.