diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotFrameworkAdapter.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotFrameworkAdapter.java index 942430d84..c3d366c2e 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotFrameworkAdapter.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotFrameworkAdapter.java @@ -1218,11 +1218,26 @@ private CompletableFuture getAppCredentials(String appId, String return CompletableFuture.completedFuture(appCredentials); } + // Create a new AppCredentials and add it to the cache. + return buildAppCredentials(appId, scope) + .thenApply(credentials -> { + appCredentialMap.put(cacheKey, credentials); + return credentials; + }); + } + + /** + * Creates an AppCredentials object for the specified appId and scope. + * + * @param appId The appId. + * @param scope The scope. + * @return An AppCredentials object. + */ + protected CompletableFuture buildAppCredentials(String appId, String scope) { return credentialProvider.getAppPassword(appId).thenApply(appPassword -> { AppCredentials credentials = channelProvider != null && channelProvider.isGovernment() ? new MicrosoftGovernmentAppCredentials(appId, appPassword, scope) : new MicrosoftAppCredentials(appId, appPassword); - appCredentialMap.put(cacheKey, credentials); return credentials; }); } diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAppCredentials.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAppCredentials.java new file mode 100644 index 000000000..22af28a2a --- /dev/null +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAppCredentials.java @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. + +package com.microsoft.bot.connector.authentication; + +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; + +/** + * AppCredentials using a certificate. + */ +public class CertificateAppCredentials extends AppCredentials { + private Authenticator authenticator; + + /** + * Initializes a new instance of the AppCredentials class. + * + * @param withOptions The options for CertificateAppCredentials. + * @throws CertificateException During Authenticator creation. + * @throws UnrecoverableKeyException During Authenticator creation. + * @throws NoSuchAlgorithmException During Authenticator creation. + * @throws KeyStoreException During Authenticator creation. + * @throws NoSuchProviderException During Authenticator creation. + * @throws IOException During Authenticator creation. + */ + public CertificateAppCredentials(CertificateAppCredentialsOptions withOptions) + throws CertificateException, + UnrecoverableKeyException, + NoSuchAlgorithmException, + KeyStoreException, + NoSuchProviderException, + IOException { + + super(withOptions.getChannelAuthTenant(), withOptions.getoAuthScope()); + + // going to create this now instead of lazy loading so we don't have some + // awkward InputStream hanging around. + authenticator = + new CertificateAuthenticator(withOptions, new OAuthConfiguration(oAuthEndpoint(), oAuthScope())); + } + + /** + * Returns a CertificateAuthenticator. + * + * @return An Authenticator object. + */ + @Override + protected Authenticator buildAuthenticator() { + return authenticator; + } +} diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAppCredentialsOptions.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAppCredentialsOptions.java new file mode 100644 index 000000000..174f71373 --- /dev/null +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAppCredentialsOptions.java @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. + +package com.microsoft.bot.connector.authentication; + +import java.io.InputStream; + +/** + * CertificateAppCredentials Options. + */ +public class CertificateAppCredentialsOptions { + private String appId; + private String channelAuthTenant; + private String oAuthScope; + private InputStream pkcs12Certificate; + private String pkcs12Password; + private boolean sendX5c = true; + + /** + * Initializes the CertificateAppCredentialsOptions with the required arguments. + * + * @param withAppId The Microsoft app ID. + * @param withPkcs12Certificate The InputStream to the pkcs certificate. + * @param withPkcs12Password The pkcs certificate password. + */ + public CertificateAppCredentialsOptions( + String withAppId, + InputStream withPkcs12Certificate, + String withPkcs12Password + ) { + this(withAppId, withPkcs12Certificate, withPkcs12Password, null, null, true); + } + + /** + * Initializes the CertificateAppCredentialsOptions. + * + * @param withAppId The Microsoft app ID. + * @param withPkcs12Certificate The InputStream to the pkcs certificate. + * @param withPkcs12Password The pkcs certificate password. + * @param withChannelAuthTenant Optional. The oauth token tenant. + * @param withOAuthScope Optional. The scope for the token. + * @param withSendX5c Specifies if the x5c claim (public key of the + * certificate) should be sent to the STS. + */ + public CertificateAppCredentialsOptions( + String withAppId, + InputStream withPkcs12Certificate, + String withPkcs12Password, + String withChannelAuthTenant, + String withOAuthScope, + boolean withSendX5c + ) { + appId = withAppId; + channelAuthTenant = withChannelAuthTenant; + oAuthScope = withOAuthScope; + pkcs12Certificate = withPkcs12Certificate; + pkcs12Password = withPkcs12Password; + sendX5c = withSendX5c; + } + + /** + * Gets the Microsfot AppId. + * + * @return The app id. + */ + public String getAppId() { + return appId; + } + + /** + * Sets the Microsfot AppId. + * + * @param withAppId The app id. + */ + public void setAppId(String withAppId) { + appId = withAppId; + } + + /** + * Gets the Channel Auth Tenant. + * + * @return The OAuth Channel Auth Tenant. + */ + public String getChannelAuthTenant() { + return channelAuthTenant; + } + + /** + * Sets the Channel Auth Tenant. + * + * @param withChannelAuthTenant The OAuth Channel Auth Tenant. + */ + public void setChannelAuthTenant(String withChannelAuthTenant) { + channelAuthTenant = withChannelAuthTenant; + } + + /** + * Gets the OAuth scope. + * + * @return The OAuthScope. + */ + public String getoAuthScope() { + return oAuthScope; + } + + /** + * Sets the OAuth scope. + * + * @param withOAuthScope The OAuthScope. + */ + public void setoAuthScope(String withOAuthScope) { + oAuthScope = withOAuthScope; + } + + /** + * Gets the InputStream to the PKCS12 certificate. + * + * @return The InputStream to the certificate. + */ + public InputStream getPkcs12Certificate() { + return pkcs12Certificate; + } + + /** + * Sets the InputStream to the PKCS12 certificate. + * + * @param withPkcs12Certificate The InputStream to the certificate. + */ + public void setPkcs12Certificate(InputStream withPkcs12Certificate) { + pkcs12Certificate = withPkcs12Certificate; + } + + /** + * Gets the pkcs12 certiciate password. + * + * @return The password for the certificate. + */ + public String getPkcs12Password() { + return pkcs12Password; + } + + /** + * Sets the pkcs12 certiciate password. + * + * @param withPkcs12Password The password for the certificate. + */ + public void setPkcs12Password(String withPkcs12Password) { + pkcs12Password = withPkcs12Password; + } + + /** + * Gets if the x5c claim (public key of the certificate) should be sent to the + * STS. + * + * @return true to send x5c. + */ + public boolean getSendX5c() { + return sendX5c; + } + + /** + * Sets if the x5c claim (public key of the certificate) should be sent to the + * STS. + * + * @param withSendX5c true to send x5c. + */ + public void setSendX5c(boolean withSendX5c) { + sendX5c = withSendX5c; + } +} diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAuthenticator.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAuthenticator.java new file mode 100644 index 000000000..e83b90d71 --- /dev/null +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CertificateAuthenticator.java @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. + +package com.microsoft.bot.connector.authentication; + +import com.microsoft.aad.msal4j.ClientCredentialFactory; +import com.microsoft.aad.msal4j.ClientCredentialParameters; +import com.microsoft.aad.msal4j.ConfidentialClientApplication; +import com.microsoft.aad.msal4j.IAuthenticationResult; +import java.io.IOException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A provider of tokens for CertificateAppCredentials. + */ +public class CertificateAuthenticator implements Authenticator { + private final ConfidentialClientApplication app; + private final ClientCredentialParameters parameters; + + /** + * Constructs an Authenticator using appId and pkcs certificate. + * + * @param withOptions The options for CertificateAppCredentials. + * @param withConfiguration The OAuthConfiguration. + * @throws CertificateException During MSAL app creation. + * @throws UnrecoverableKeyException During MSAL app creation. + * @throws NoSuchAlgorithmException During MSAL app creation. + * @throws KeyStoreException During MSAL app creation. + * @throws NoSuchProviderException During MSAL app creation. + * @throws IOException During MSAL app creation. + */ + public CertificateAuthenticator(CertificateAppCredentialsOptions withOptions, OAuthConfiguration withConfiguration) + throws CertificateException, + UnrecoverableKeyException, + NoSuchAlgorithmException, + KeyStoreException, + NoSuchProviderException, + IOException { + + app = ConfidentialClientApplication.builder( + withOptions.getAppId(), + ClientCredentialFactory.createFromCertificate( + withOptions.getPkcs12Certificate(), + withOptions.getPkcs12Password()) + ) + .authority(withConfiguration.getAuthority()).sendX5c(withOptions.getSendX5c()).build(); + + parameters = ClientCredentialParameters.builder(Collections.singleton(withConfiguration.getScope())).build(); + } + + /** + * Returns a token. + * + * @return The MSAL token result. + */ + @Override + public CompletableFuture acquireToken() { + return app.acquireToken(parameters) + .exceptionally( + exception -> { + // wrapping whatever msal throws into our own exception + throw new AuthenticationException(exception); + } + ); + } +} diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CredentialsAuthenticator.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CredentialsAuthenticator.java index f4e18086b..1bfbe6c03 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CredentialsAuthenticator.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/CredentialsAuthenticator.java @@ -16,8 +16,8 @@ * An Authenticator using app id and password. */ public class CredentialsAuthenticator implements Authenticator { - private ConfidentialClientApplication app; - private ClientCredentialParameters parameters; + private final ConfidentialClientApplication app; + private final ClientCredentialParameters parameters; /** * Constructs an Authenticator using appId and appPassword.