diff --git a/authorization/msi-auth-token-provider-jar/pom.xml b/authorization/msi-auth-token-provider-jar/pom.xml
new file mode 100644
index 000000000000..7737c0d60842
--- /dev/null
+++ b/authorization/msi-auth-token-provider-jar/pom.xml
@@ -0,0 +1,122 @@
+
+
+ 4.0.0
+ com.microsoft.azure.msi_auth_token_provider
+
+ azure-authentication-msi-token-provider
+ jar
+ 1.0.0-Beta-1
+
+ Azure Java Client MSI Authorization Token Provoider Library
+ This package contains the MSI token provider classes for Azure.
+ https://github.com/Azure/azure-sdk-for-java
+
+
+
+ The MIT License (MIT)
+ http://opensource.org/licenses/MIT
+ repo
+
+
+
+
+ scm:git:https://github.com/Azure/azure-sdk-for-java
+ scm:git:git@github.com:Azure/azure-sdk-for-java.git
+ HEAD
+
+
+
+ UTF-8
+
+
+
+
+
+ microsoft
+ Microsoft
+
+
+
+
+
+ junit
+ junit
+ test
+ 4.12
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.0
+
+
+
+ true
+ true
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ jar-with-dependencies
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.0.0
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+ 8
+ 8
+ true
+ true
+
+ true
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.8
+
+ *.implementation.*;*.utils.*;com.microsoft.schemas._2003._10.serialization;*.blob.core.search
+
+
+ /**
+
* Copyright (c) Microsoft Corporation. All rights reserved.
+
* Licensed under the MIT License. See License.txt in the project root for
+
* license information.
+
*/
+ ]]>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/authorization/msi-auth-token-provider-jar/readme.md b/authorization/msi-auth-token-provider-jar/readme.md
new file mode 100644
index 000000000000..84893d844623
--- /dev/null
+++ b/authorization/msi-auth-token-provider-jar/readme.md
@@ -0,0 +1,68 @@
+# What is this?
+
+The "msi-auth-token-provider" jar is a library that enables :
+* Azure VMs and container instances and
+* Web Apps (funcitons included)
+Retrieve authentication tokens for syatem/user assigned [managed identities](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview).
+
+This is a light weight library that does not have many dependencies.
+
+# Usage
+## Dependency
+Take a dependency on the jar in you pom file like follows
+```xml
+
+
+ com.microsoft.azure.msi_auth_token_provider
+ azure-authentication-msi-token-provider
+ 1.0.0-beta
+
+
+```
+
+## Getting the token
+
+Add the folowing import statement to get in all the classes in the jar
+
+```java
+import com.microsoft.azure.msiAuthTokenProvider.*;
+```
+
+### Getting a token for system assigned identity
+Use the following code to get the auth token for System assigned identity :
+
+``` java
+...
+ MSICredentials credsProvider = MSICredentials.getMSICredentials();
+ MSIToken token = credsProvider.getToken(null);
+ String tokenValue = token.accessToken();
+...
+```
+
+### Getting a token for user assigned identity
+
+#### Using the client Id for the user assigned identity :
+Use the following code to get the auth token for an User assigned identity :
+```java
+...
+ MSICredentials credsProvider = MSICredentials.getMSICredentials();
+ credsProvider.updateClientId(clientId);
+ MSIToken token = credsProvider.getToken(null);
+ String tokenValue = token.accessToken();
+...
+```
+
+Where `clientId` is retrieved from the User Assigned Identity (This is currently only supported from within the portal).
+
+#### Using the object Id for the user assigned identity :
+Use the following code to get the auth token for an User assigned identity :
+```java
+...
+ MSICredentials credsProvider = MSICredentials.getMSICredentials();
+ credsProvider.updateObjectId(objectId);
+ MSIToken token = credsProvider.getToken(null);
+ String tokenValue = token.accessToken();
+...
+```
+
+Where `objectId` is retrieved from the User Assigned Identity (This is currently only supported from within the portal).
\ No newline at end of file
diff --git a/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/AzureMSICredentialException.java b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/AzureMSICredentialException.java
new file mode 100644
index 000000000000..a25aa3c6cf2f
--- /dev/null
+++ b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/AzureMSICredentialException.java
@@ -0,0 +1,15 @@
+package com.microsoft.azure.msiAuthTokenProvider;
+
+public class AzureMSICredentialException extends Exception{
+ AzureMSICredentialException(String message) {
+ super(message);
+ }
+
+ AzureMSICredentialException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ AzureMSICredentialException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIConfigurationForAppService.java b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIConfigurationForAppService.java
new file mode 100644
index 000000000000..2cddac281d2c
--- /dev/null
+++ b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIConfigurationForAppService.java
@@ -0,0 +1,163 @@
+/**
+ * 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.azure.msiAuthTokenProvider;
+
+/**
+ * Defines the configuration to be used for retrieving access token from
+ * within an app-service with system assigned MSI enabled.
+ */
+public class MSIConfigurationForAppService {
+ private final String managementEndpoint;
+ private String resource;
+ private String msiEndpoint;
+ private String msiSecret;
+ private String clientId;
+ private String objectId;
+
+ /**
+ * Creates MSIConfigurationForAppService.
+ *
+ * @param managementEndpoint azure management endpoint
+ */
+ public MSIConfigurationForAppService(String managementEndpoint) {
+ this.managementEndpoint = managementEndpoint;
+ }
+
+ /**
+ * Creates MSIConfigurationForAppService.
+ */
+ public MSIConfigurationForAppService() {
+ this(MSICredentials.DEFAULT_AZURE_MANAGEMENT_ENDPOINT);
+ }
+
+ /**
+ * @return the azure management Endpoint.
+ */
+ public String managementEndpoint() {
+ return this.managementEndpoint;
+ }
+
+ /**
+ * @return the audience identifying who will consume the token.
+ */
+ public String resource() {
+ if (this.resource == null) {
+ this.resource = this.managementEndpoint;
+ }
+ return this.resource;
+ }
+
+ /**
+ * @return the endpoint from which token needs to be retrieved.
+ */
+ public String msiEndpoint() {
+ if (this.msiEndpoint == null) {
+ this.msiEndpoint = System.getenv("MSI_ENDPOINT");
+ }
+ return this.msiEndpoint;
+ }
+
+ /**
+ * @return the secret to use to retrieve the token.
+ */
+ public String msiSecret() {
+ if (this.msiSecret == null) {
+ this.msiSecret = System.getenv("MSI_SECRET");
+ }
+ return this.msiSecret;
+ }
+
+ /**
+ * @return the object id
+ */
+ public String msiObjectId() {
+ return this.objectId;
+ }
+
+ /**
+ * @return the client id
+ */
+ public String msiClientId() {
+ return this.clientId;
+ }
+
+ /**
+ * Specifies the token audience.
+ *
+ * @param resource the audience of the token.
+ *
+ * @return MSIConfigurationForAppService
+ */
+ public MSIConfigurationForAppService withResource(String resource) {
+ this.resource = resource;
+ return this;
+ }
+
+ /**
+ * Specifies the endpoint from which token needs to retrieved.
+ *
+ * @param msiEndpoint the token endpoint.
+ *
+ * @return MSIConfigurationForAppService
+ */
+ public MSIConfigurationForAppService withMsiEndpoint(String msiEndpoint) {
+ this.msiEndpoint = msiEndpoint;
+ return this;
+ }
+
+ /**
+ * Specify the client Id (to be used or user assigned identities)
+ * @param clientId the client ID fot eh user assigned identity
+ * @return MSIConfigurationForAppService
+ */
+ public MSIConfigurationForAppService withClientId(String clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ /**
+ * Specify the object Id (to be used or user assigned identities)
+ * @param objectId the object ID fot eh user assigned identity
+ * @return MSIConfigurationForAppService
+ */
+ public MSIConfigurationForAppService withObjectId(String objectId) {
+ this.objectId = objectId;
+ return this;
+ }
+
+ /**
+ * Specifies secret to use to retrieve the token.
+ *
+ * @param msiSecret the secret.
+ *
+ * @return MSIConfigurationForAppService
+ */
+ public MSIConfigurationForAppService withMsiSecret(String msiSecret) {
+ this.msiSecret = msiSecret;
+ return this;
+ }
+
+ @Override
+ public MSIConfigurationForAppService clone() {
+ MSIConfigurationForAppService copy = new MSIConfigurationForAppService(this.managementEndpoint);
+ if (this.resource() != null) {
+ copy.withResource(this.resource());
+ }
+ if (this.msiEndpoint() != null) {
+ copy.withMsiEndpoint(this.msiEndpoint());
+ }
+ if (this.msiSecret() != null) {
+ copy.withMsiSecret(this.msiSecret());
+ }
+ if (this.msiClientId() != null) {
+ copy.withClientId(this.msiClientId());
+ }
+ if (this.msiObjectId() != null) {
+ copy.withObjectId(this.msiObjectId());
+ }
+ return copy;
+ }
+}
\ No newline at end of file
diff --git a/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIConfigurationForVirtualMachine.java b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIConfigurationForVirtualMachine.java
new file mode 100644
index 000000000000..1a179b719497
--- /dev/null
+++ b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIConfigurationForVirtualMachine.java
@@ -0,0 +1,188 @@
+/**
+ * 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.azure.msiAuthTokenProvider;
+
+/**
+ * Defines the configuration to be used for retrieving access token from
+ * within a VM with user assigned or system assigned MSI enabled.
+ */
+public class MSIConfigurationForVirtualMachine {
+ private final String managementEndpoint;
+ private String resource;
+ private MSITokenSource tokenSource = MSITokenSource.IMDS_ENDPOINT;
+ private String objectId;
+ private String clientId;
+ private String identityId;
+ private int maxRetry = -1;
+
+ /**
+ * Creates MSIConfigurationForVirtualMachine.
+ *
+ * @param managementEndpoint azure management endpoint
+ */
+ public MSIConfigurationForVirtualMachine(String managementEndpoint) {
+ this.managementEndpoint = managementEndpoint;
+ }
+
+ /**
+ * Creates MSIConfigurationForVirtualMachine.
+ */
+ public MSIConfigurationForVirtualMachine() {
+ this(MSICredentials.DEFAULT_AZURE_MANAGEMENT_ENDPOINT);
+ }
+
+ /**
+ * @return the azure management Endpoint.
+ */
+ public String managementEndpoint() {
+ return this.managementEndpoint;
+ }
+
+ /**
+ * @return the token retrieval source (either MSI extension running in VM or IMDS service).
+ */
+ public MSITokenSource tokenSource() {
+ if (this.tokenSource == null) {
+ this.tokenSource = MSITokenSource.IMDS_ENDPOINT;
+ }
+ return this.tokenSource;
+ }
+ /**
+ * @return the audience identifying who will consume the token.
+ */
+ public String resource() {
+ if (this.resource == null) {
+ this.resource = this.managementEndpoint;
+ }
+ return this.resource;
+ }
+ /**
+ * @return the principal id of user assigned or system assigned identity.
+ */
+ public String objectId() {
+ return this.objectId;
+ }
+ /**
+ * @return the client id of user assigned or system assigned identity.
+ */
+ public String clientId() {
+ return this.clientId;
+ }
+ /**
+ * @return the ARM resource id of the user assigned identity resource.
+ */
+ public String identityId() {
+ return this.identityId;
+ }
+
+ /**
+ * @return the maximum retries allowed.
+ */
+ public int maxRetry() {
+ return this.maxRetry;
+ }
+
+ /**
+ * Specifies the token retrieval source.
+ *
+ * @param tokenSource the source of token
+ *
+ * @return MSIConfigurationForVirtualMachine
+ */
+ public MSIConfigurationForVirtualMachine withTokenSource(MSITokenSource tokenSource) {
+ this.tokenSource = tokenSource;
+ return this;
+ }
+
+ /**
+ * Specifies the token audience.
+ *
+ * @param resource the audience of the token.
+ *
+ * @return MSIConfigurationForVirtualMachine
+ */
+ public MSIConfigurationForVirtualMachine withResource(String resource) {
+ this.resource = resource;
+ return this;
+ }
+
+ /**
+ * specifies the principal id of user assigned or system assigned identity.
+ *
+ * @param objectId the object (principal) id
+ * @return MSIConfigurationForVirtualMachine
+ */
+ public MSIConfigurationForVirtualMachine withObjectId(String objectId) {
+ this.objectId = objectId;
+ return this;
+ }
+
+ /**
+ * Specifies the client id of user assigned or system assigned identity.
+ *
+ * @param clientId the client id
+ * @return MSIConfigurationForVirtualMachine
+ */
+ public MSIConfigurationForVirtualMachine withClientId(String clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ /**
+ * Specifies the ARM resource id of the user assigned identity resource.
+ *
+ * @param identityId the identity ARM id
+ * @return MSIConfigurationForVirtualMachine
+ */
+ public MSIConfigurationForVirtualMachine withIdentityId(String identityId) {
+ this.identityId = identityId;
+ return this;
+ }
+
+ /**
+ * Specifies the the maximum retries allowed.
+ *
+ * @param maxRetry max retry count
+ * @return MSIConfigurationForVirtualMachine
+ */
+ public MSIConfigurationForVirtualMachine withMaxRetry(int maxRetry) {
+ this.maxRetry = maxRetry;
+ return this;
+ }
+
+ @Override
+ public MSIConfigurationForVirtualMachine clone() {
+ MSIConfigurationForVirtualMachine copy = new MSIConfigurationForVirtualMachine(this.managementEndpoint);
+ if (this.clientId() != null) {
+ copy.withClientId(this.clientId());
+ }
+ if (this.identityId() != null) {
+ copy.withIdentityId(this.identityId());
+ }
+ if (this.objectId() != null) {
+ copy.withObjectId(this.objectId());
+ }
+ if (this.resource() != null) {
+ copy.withResource(this.resource());
+ }
+ if (this.tokenSource() != null) {
+ copy.withTokenSource(this.tokenSource());
+ }
+ copy.withMaxRetry(this.maxRetry());
+ return copy;
+ }
+
+
+ /**
+ * The source of MSI token.
+ */
+ public enum MSITokenSource {
+ /**
+ * Indicate that token should be retrieved from IMDS service.
+ */
+ IMDS_ENDPOINT
+ }
+}
\ No newline at end of file
diff --git a/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSICredentials.java b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSICredentials.java
new file mode 100644
index 000000000000..eb32fe913e0e
--- /dev/null
+++ b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSICredentials.java
@@ -0,0 +1,374 @@
+/**
+ * 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.azure.msiAuthTokenProvider;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.SocketException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * Managed Service Identity token based credentials for use with a REST Service Client.
+ */
+public final class MSICredentials{
+ public static final String DEFAULT_AZURE_MANAGEMENT_ENDPOINT = "https://management.core.windows.net/";
+ //
+ private final List retrySlots = new ArrayList<>();
+ //
+ private final MSIConfigurationForVirtualMachine configForVM;
+ private final MSIConfigurationForAppService configForAppService;
+ private final HostType hostType;
+ private final int maxRetry;
+ private static final int MAX_RETRY_DEFAULT_LIMIT = 20;
+
+
+ class ActiveDirectoryAuthentication {
+ static final String AZURE_REST_MSI_URL = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01";
+ static final String ACCESS_TOKEN_IDENTIFIER = "\"access_token\":\"";
+ static final String ACCESS_TOKEN_TYPE_IDENTIFIER = "\"token_type\":\"";
+ static final String ACCESS_TOKEN_EXPIRES_IN_IDENTIFIER = "\"expires_in\":\"";
+ static final String ACCESS_TOKEN_EXPIRES_ON_IDENTIFIER = "\"expires_on\":\"";
+ static final String ACCESS_TOKEN_EXPIRES_ON_DATE_FORMAT = "M/d/yyyy h:mm:ss a X";
+ }
+
+ /**
+ * This method checks if the env vars "MSI_ENDPOINT" and "MSI_SECRET" exist. If they do, we return the msi creds class for APP Svcs
+ * otherwise we return one for VM
+ *
+ * @return MSICredentials
+ */
+ public static MSICredentials getMSICredentials() {
+ return getMSICredentials(DEFAULT_AZURE_MANAGEMENT_ENDPOINT);
+ }
+
+ /**
+ * This method checks if the env vars "MSI_ENDPOINT" and "MSI_SECRET" exist. If they do, we return the msi creds class for APP Svcs
+ * otherwise we return one for VM
+ *
+ * @param managementEndpoint Management endpoint in Azure
+ * @return MSICredentials
+ */
+ public static MSICredentials getMSICredentials(String managementEndpoint) {
+ //check if we are running in a web app
+ String websiteName = System.getenv("WEBSITE_SITE_NAME");
+
+ if (websiteName != null && !websiteName.isEmpty()) {
+ // We are in a web app...
+ MSIConfigurationForAppService config = new MSIConfigurationForAppService(managementEndpoint);
+ return forAppService(config);
+ } else {
+ //We are in a vm/container
+ MSIConfigurationForVirtualMachine config = new MSIConfigurationForVirtualMachine(managementEndpoint);
+ return forVirtualMachine(config);
+ }
+ }
+
+ /**
+ * Creates MSICredentials for application running on MSI enabled virtual machine.
+ *
+ * @return MSICredentials
+ */
+ public static MSICredentials forVirtualMachine() {
+ return new MSICredentials(new MSIConfigurationForVirtualMachine());
+ }
+
+ /**
+ * Creates MSICredentials for application running on MSI enabled virtual machine.
+ *
+ * @param config the configuration to be used for token request.
+ * @return MSICredentials
+ */
+ public static MSICredentials forVirtualMachine(MSIConfigurationForVirtualMachine config) {
+ return new MSICredentials(config.clone());
+ }
+
+ /**
+ * Creates MSICredentials for application running on MSI enabled app service.
+ *
+ * @return MSICredentials
+ */
+ public static MSICredentials forAppService() {
+ return new MSICredentials(new MSIConfigurationForAppService());
+ }
+
+ /**
+ * Creates MSICredentials for application running on MSI enabled app service.
+ *
+ * @param config the configuration to be used for token request.
+ * @return MSICredentials
+ */
+ public static MSICredentials forAppService(MSIConfigurationForAppService config) {
+ return new MSICredentials(config.clone());
+ }
+
+ private MSICredentials(MSIConfigurationForVirtualMachine config) {
+ this.configForVM = config;
+ this.configForAppService = null;
+ this.hostType = HostType.VIRTUAL_MACHINE;
+ this.maxRetry = config.maxRetry() < 0 ? MAX_RETRY_DEFAULT_LIMIT : config.maxRetry();
+ // Simplified variant of https://en.wikipedia.org/wiki/Exponential_backoff
+ for (int x = 0; x < this.maxRetry; x++) {
+ this.retrySlots.add(500 * ((2 << 1) - 1) / 1000);
+ }
+ }
+
+ private MSICredentials(MSIConfigurationForAppService config) {
+ this.configForAppService = config;
+ this.configForVM = null;
+ this.hostType = HostType.APP_SERVICE;
+ this.maxRetry = -1;
+ }
+
+ /**
+ * Updates the client Id for the associated config (vm or app)
+ * Specifying a null value will clear out the old value
+ * @param clientId
+ */
+ public void updateClientId(String clientId) {
+ if (configForVM != null) {
+ configForVM.withClientId(clientId);
+ } else {
+ configForAppService.withClientId(clientId);
+ }
+ }
+
+ /**
+ * Updates the object Id for the associated config (vm or app)
+ * Specifying a null value will clear out the old value
+ * @param objectId
+ */
+ public void updateObjectId(String objectId) {
+ if (configForVM != null) {
+ configForVM.withObjectId(objectId);
+ } else {
+ configForAppService.withObjectId(objectId);
+ }
+ }
+
+ public MSIToken getToken(String tokenAudience) throws IOException, AzureMSICredentialException{
+ switch (hostType) {
+ case VIRTUAL_MACHINE:
+ return this.retrieveTokenFromIDMSWithRetry(tokenAudience == null ? this.configForVM.resource() : tokenAudience);
+ case APP_SERVICE:
+ return this.getTokenForAppService(tokenAudience);
+ default:
+ throw new IllegalArgumentException("unknown host type:" + hostType);
+ }
+ }
+
+ private MSIToken getTokenForAppService(String tokenAudience) throws IOException, AzureMSICredentialException {
+ String urlString = null;
+
+ if (this.configForAppService.msiEndpoint() == null || this.configForAppService.msiEndpoint().isEmpty()) {
+ //the web app does not have MSI set, return file not found
+ throw new FileNotFoundException("Managed identity not found/configured");
+ }
+
+ if (this.configForAppService.msiClientId() != null && !this.configForAppService.msiClientId().isEmpty()) {
+ urlString = String.format("%s?resource=%s&clientid=%s&api-version=2017-09-01", this.configForAppService.msiEndpoint(),
+ tokenAudience == null ? this.configForAppService.resource() : tokenAudience,
+ this.configForAppService.msiClientId());
+ } else if (this.configForAppService.msiObjectId() != null && !this.configForAppService.msiObjectId().isEmpty()) {
+ urlString = String.format("%s?resource=%s&objectid=%s&api-version=2017-09-01", this.configForAppService.msiEndpoint(),
+ tokenAudience == null ? this.configForAppService.resource() : tokenAudience,
+ this.configForAppService.msiObjectId());
+ } else {
+ urlString = String.format("%s?resource=%s&api-version=2017-09-01", this.configForAppService.msiEndpoint(),
+ tokenAudience == null ? this.configForAppService.resource() : tokenAudience);
+ }
+ URL url = new URL(urlString);
+ HttpURLConnection connection = null;
+
+ try {
+ connection = (HttpURLConnection) url.openConnection();
+
+ connection.setRequestMethod("GET");
+ connection.setRequestProperty("Secret", this.configForAppService.msiSecret());
+ connection.setRequestProperty("Metadata", "true");
+
+ connection.connect();
+
+ InputStream stream = connection.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"), 100);
+ String result = reader.readLine();
+
+ return getMsiTokenFromResult(result, HostType.APP_SERVICE);
+ } catch (IOException ioEx){
+ if (ioEx.getMessage().contains("Server returned HTTP response code: 400 for URL")) {
+ throw new AzureMSICredentialException("Managed identity not found/configured", ioEx);
+ } else throw ioEx;
+ } catch (Exception e){
+ if (e.getCause()!= null && e.getCause() instanceof SocketException && e.getCause().getMessage().contains("Permission denied: connect")) {
+ throw new AzureMSICredentialException("Managed identity not found/configured", e);
+ } else throw e;
+ } finally {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+
+ private MSIToken retrieveTokenFromIDMSWithRetry(String tokenAudience) throws AzureMSICredentialException, IOException {
+ StringBuilder payload = new StringBuilder();
+ final int imdsUpgradeTimeInMs = 70 * 1000;
+
+ //
+ try {
+ payload.append("api-version");
+ payload.append("=");
+ payload.append(URLEncoder.encode("2018-02-01", "UTF-8"));
+ payload.append("&");
+ payload.append("resource");
+ payload.append("=");
+ payload.append(URLEncoder.encode(tokenAudience, "UTF-8"));
+ if (this.configForVM.objectId() != null) {
+ payload.append("&");
+ payload.append("object_id");
+ payload.append("=");
+ payload.append(URLEncoder.encode(this.configForVM.objectId(), "UTF-8"));
+ } else if (this.configForVM.clientId() != null) {
+ payload.append("&");
+ payload.append("client_id");
+ payload.append("=");
+ payload.append(URLEncoder.encode(this.configForVM.clientId(), "UTF-8"));
+ } else if (this.configForVM.identityId() != null) {
+ payload.append("&");
+ payload.append("msi_res_id");
+ payload.append("=");
+ payload.append(URLEncoder.encode(this.configForVM.identityId(), "UTF-8"));
+ }
+ } catch (IOException exception) {
+ throw new AzureMSICredentialException(exception);
+ }
+
+ int retry = 1;
+ while (retry <= maxRetry) {
+ URL url = new URL(String.format("http://169.254.169.254/metadata/identity/oauth2/token?%s", payload.toString()));
+ //
+ HttpURLConnection connection = null;
+ //
+ try {
+ connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ connection.setRequestProperty("Metadata", "true");
+ connection.connect();
+ InputStream stream = connection.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"), 100);
+ String result = reader.readLine();
+ return getMsiTokenFromResult(result, HostType.VIRTUAL_MACHINE);
+ } catch (Exception exception) {
+ int responseCode = connection.getResponseCode();
+ if (responseCode == 410 || responseCode == 429 || responseCode == 404 || (responseCode >= 500 && responseCode <= 599)) {
+ int retryTimeoutInMs = retrySlots.get(new Random().nextInt(retry));
+ // Error code 410 indicates IMDS upgrade is in progress, which can take up to 70s
+ //
+ retryTimeoutInMs = (responseCode == 410 && retryTimeoutInMs < imdsUpgradeTimeInMs) ? imdsUpgradeTimeInMs : retryTimeoutInMs;
+ retry++;
+ if (retry > maxRetry) {
+ break;
+ } else {
+ sleep(retryTimeoutInMs);
+ }
+ } else {
+ throw new AzureMSICredentialException("Couldn't acquire access token from IMDS, verify your objectId, clientId or msiResourceId", exception);
+ }
+ } finally {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+ //
+ if (retry > maxRetry) {
+ throw new AzureMSICredentialException(String.format("MSI: Failed to acquire tokens after retrying %s times", maxRetry));
+ }
+ return null;
+ }
+
+ private static MSIToken getMsiTokenFromResult(String result, HostType hostType) throws AzureMSICredentialException{
+ try {
+ return new MSIToken(getTokenFromResult(result), getTokenTypeFromResult(result), getExpiryTimeFromResult(result, hostType));
+ } catch (ParseException pe) {
+ throw new AzureMSICredentialException(pe.getMessage(), pe);
+ }
+ }
+
+ private static String getTokenFromResult(String result) {
+ int startIndex_AT = result.indexOf(ActiveDirectoryAuthentication.ACCESS_TOKEN_IDENTIFIER)
+ + ActiveDirectoryAuthentication.ACCESS_TOKEN_IDENTIFIER.length();
+
+ return (result.substring(startIndex_AT, result.indexOf("\"", startIndex_AT + 1)));
+ }
+
+ private static String getTokenTypeFromResult(String result) {
+ int startIndex_AT = result.indexOf(ActiveDirectoryAuthentication.ACCESS_TOKEN_TYPE_IDENTIFIER)
+ + ActiveDirectoryAuthentication.ACCESS_TOKEN_TYPE_IDENTIFIER.length();
+
+ return (result.substring(startIndex_AT, result.indexOf("\"", startIndex_AT + 1)));
+ }
+
+ private int getIndexInString(String sourceText, String subString) throws ParseException {
+ int index = sourceText.indexOf(subString);
+
+ if (index < 0) {
+ throw new ParseException("Text to search not found in source text", 0);
+ }
+
+ return index;
+ }
+
+ private static Date getExpiryTimeFromResult(String result, HostType hostType) throws ParseException {
+ Calendar cal = new Calendar.Builder().setInstant(new Date()).build();
+ if (hostType == HostType.VIRTUAL_MACHINE) {
+ int startIndex_ATX = result
+ .indexOf(ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_IN_IDENTIFIER)
+ + ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_IN_IDENTIFIER.length();
+ String accessTokenExpiry = result.substring(startIndex_ATX,
+ result.indexOf("\"", startIndex_ATX + 1));
+ cal.add(Calendar.SECOND, Integer.parseInt(accessTokenExpiry));
+ } else {
+ int startIndex_ATX = result
+ .indexOf(ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_ON_IDENTIFIER)
+ + ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_ON_IDENTIFIER.length();
+ String accessTokenExpiry = result.substring(startIndex_ATX,
+ result.indexOf("\"", startIndex_ATX + 1));
+
+ DateFormat df = new SimpleDateFormat(
+ ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_ON_DATE_FORMAT);
+ cal = new Calendar.Builder().setInstant(df.parse(accessTokenExpiry)).build();
+ }
+
+ return cal.getTime();
+ }
+
+ private static void sleep(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * The host in which application is running.
+ */
+ private enum HostType {
+ /**
+ * indicate that host is an Azure virtual machine.
+ */
+ VIRTUAL_MACHINE,
+ /**
+ * indicate that host is an Azure app-service instance.
+ */
+ APP_SERVICE
+ }
+}
\ No newline at end of file
diff --git a/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIToken.java b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIToken.java
new file mode 100644
index 000000000000..3903f824d6f5
--- /dev/null
+++ b/authorization/msi-auth-token-provider-jar/src/main/java/com/microsoft/azure/msiAuthTokenProvider/MSIToken.java
@@ -0,0 +1,49 @@
+/**
+ * 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.azure.msiAuthTokenProvider;
+
+
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * Type representing response from the local MSI token provider.
+ */
+public class MSIToken {
+ private String tokenType;
+
+ private String accessToken;
+
+ private Date expiresOn;
+
+ public MSIToken(String accessToken, String tokenType, Date expiresOn) {
+ this.accessToken = accessToken;
+ this.tokenType = tokenType;
+ this.expiresOn = expiresOn;
+ }
+
+ public String accessToken() {
+ return accessToken;
+ }
+
+ public String tokenType() {
+ return tokenType;
+ }
+
+ public Date expiresOn() {
+ return expiresOn;
+ }
+
+ public boolean isExpired() {
+ //get time now
+ Calendar today = Calendar.getInstance();
+ today.set(Calendar.HOUR_OF_DAY, 0);
+ Date timeNow = today.getTime();
+
+ return (timeNow.after(expiresOn));
+ }
+}
\ No newline at end of file