Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. See License.txt in the project root for
license information.
/**
* 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.credentials;
import com.microsoft.aad.adal4j.AsymmetricKeyCredential;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationException;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import com.microsoft.azure.management.apigeneration.Beta;
import com.microsoft.azure.management.apigeneration.Beta.SinceVersion;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Token based credentials to authenticate an application on behalf of a user.
/**
* Token based credentials to authenticate an application on behalf of a user.
*/
@Beta(SinceVersion.V1_2_0)
public class DelegatedTokenCredentials extends AzureTokenCredentials {
A mapping from resource endpoint to its cached access token. /** A mapping from resource endpoint to its cached access token. */
private Map<String, AuthenticationResult> tokens;
private String redirectUrl;
private String authorizationCode;
private ApplicationTokenCredentials applicationCredentials;
Initializes a new instance of the DelegatedTokenCredentials.
Params: - applicationCredentials – the credentials representing a service principal
- redirectUrl – the URL to redirect to after authentication in Active Directory
/**
* Initializes a new instance of the DelegatedTokenCredentials.
*
* @param applicationCredentials the credentials representing a service principal
* @param redirectUrl the URL to redirect to after authentication in Active Directory
*/
public DelegatedTokenCredentials(ApplicationTokenCredentials applicationCredentials, String redirectUrl) {
super(applicationCredentials.environment(), applicationCredentials.domain()); // defer token acquisition
this.applicationCredentials = applicationCredentials;
this.tokens = new ConcurrentHashMap<>();
this.redirectUrl = redirectUrl;
}
Initializes a new instance of the DelegatedTokenCredentials, with a pre-acquired oauth2 authorization code.
Params: - applicationCredentials – the credentials representing a service principal
- redirectUrl – the URL to redirect to after authentication in Active Directory
- authorizationCode – the oauth2 authorization code
/**
* Initializes a new instance of the DelegatedTokenCredentials, with a pre-acquired oauth2 authorization code.
*
* @param applicationCredentials the credentials representing a service principal
* @param redirectUrl the URL to redirect to after authentication in Active Directory
* @param authorizationCode the oauth2 authorization code
*/
public DelegatedTokenCredentials(ApplicationTokenCredentials applicationCredentials, String redirectUrl, String authorizationCode) {
this(applicationCredentials, redirectUrl);
this.authorizationCode = authorizationCode;
}
Creates a new instance of the DelegatedTokenCredentials from an auth file.
Params: - authFile – The credentials based on the file
- redirectUrl – the URL to redirect to after authentication in Active Directory
Throws: - IOException – exception thrown from file access errors.
Returns: a new delegated token credentials
/**
* Creates a new instance of the DelegatedTokenCredentials from an auth file.
*
* @param authFile The credentials based on the file
* @param redirectUrl the URL to redirect to after authentication in Active Directory
* @return a new delegated token credentials
* @throws IOException exception thrown from file access errors.
*/
public static DelegatedTokenCredentials fromFile(File authFile, String redirectUrl) throws IOException {
return new DelegatedTokenCredentials(ApplicationTokenCredentials.fromFile(authFile), redirectUrl);
}
Creates a new instance of the DelegatedTokenCredentials from an auth file,
with a pre-acquired oauth2 authorization code.
Params: - authFile – The credentials based on the file
- redirectUrl – the URL to redirect to after authentication in Active Directory
- authorizationCode – the oauth2 authorization code
Throws: - IOException – exception thrown from file access errors.
Returns: a new delegated token credentials
/**
* Creates a new instance of the DelegatedTokenCredentials from an auth file,
* with a pre-acquired oauth2 authorization code.
*
* @param authFile The credentials based on the file
* @param redirectUrl the URL to redirect to after authentication in Active Directory
* @param authorizationCode the oauth2 authorization code
* @return a new delegated token credentials
* @throws IOException exception thrown from file access errors.
*/
public static DelegatedTokenCredentials fromFile(File authFile, String redirectUrl, String authorizationCode) throws IOException {
return new DelegatedTokenCredentials(ApplicationTokenCredentials.fromFile(authFile), redirectUrl, authorizationCode);
}
Returns: the active directory application client id
/**
* @return the active directory application client id
*/
public String clientId() {
return applicationCredentials.clientId();
}
Returns: the URL to authenticate through OAuth2
/**
* @return the URL to authenticate through OAuth2
*/
public String generateAuthenticationUrl() {
return String.format("%s/%s/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&response_mode=query&state=%s",
environment().activeDirectoryEndpoint(), domain(), clientId(), this.redirectUrl, UUID.randomUUID());
}
Generate the URL to authenticate through OAuth2.
Params: - responseMode – the method that should be used to send the resulting token back to your app
- state – a value included in the request that is also returned in the token response
Returns: the URL to authenticate through OAuth2
/**
* Generate the URL to authenticate through OAuth2.
*
* @param responseMode the method that should be used to send the resulting token back to your app
* @param state a value included in the request that is also returned in the token response
* @return the URL to authenticate through OAuth2
*/
public String generateAuthenticationUrl(ResponseMode responseMode, String state) {
return String.format("%s/%s/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&response_mode=%s&state=%s",
environment().activeDirectoryEndpoint(), domain(), clientId(), this.redirectUrl, responseMode.value, state);
}
Set the authorization code acquired returned to the redirect URL.
Params: - authorizationCode – the oauth2 authorization code
/**
* Set the authorization code acquired returned to the redirect URL.
* @param authorizationCode the oauth2 authorization code
*/
public void setAuthorizationCode(String authorizationCode) {
this.authorizationCode = authorizationCode;
}
@Override
public synchronized String getToken(String resource) throws IOException {
// Find exact match for the resource
AuthenticationResult authenticationResult = tokens.get(resource);
// Return if found and not expired
if (authenticationResult != null && authenticationResult.getExpiresOnDate().after(new Date())) {
return authenticationResult.getAccessToken();
}
// If found then refresh
boolean shouldRefresh = authenticationResult != null;
// If not found for the resource, but is MRRT then also refresh
if (authenticationResult == null && !tokens.isEmpty()) {
authenticationResult = new ArrayList<>(tokens.values()).get(0);
shouldRefresh = authenticationResult.isMultipleResourceRefreshToken();
}
// Refresh
if (shouldRefresh) {
authenticationResult = acquireAccessTokenFromRefreshToken(resource, authenticationResult.getRefreshToken());
}
// If refresh fails or not refreshable, acquire new token
if (authenticationResult == null) {
authenticationResult = acquireNewAccessToken(resource);
}
tokens.put(resource, authenticationResult);
return authenticationResult.getAccessToken();
}
AuthenticationResult acquireNewAccessToken(String resource) throws IOException {
if (authorizationCode == null) {
throw new IllegalArgumentException("You must acquire an authorization code by redirecting to the authentication URL");
}
String authorityUrl = this.environment().activeDirectoryEndpoint() + this.domain();
ExecutorService executor = Executors.newSingleThreadExecutor();
AuthenticationContext context = new AuthenticationContext(authorityUrl, false, executor);
if (proxy() != null) {
context.setProxy(proxy());
}
try {
if (applicationCredentials.clientSecret() != null) {
return context.acquireTokenByAuthorizationCode(
authorizationCode,
new URI(redirectUrl),
new ClientCredential(applicationCredentials.clientId(), applicationCredentials.clientSecret()),
resource, null).get();
} else if (applicationCredentials.clientCertificate() != null && applicationCredentials.clientCertificatePassword() != null) {
return context.acquireTokenByAuthorizationCode(
authorizationCode,
new URI(redirectUrl),
AsymmetricKeyCredential.create(
applicationCredentials.clientId(),
new ByteArrayInputStream(applicationCredentials.clientCertificate()),
applicationCredentials.clientCertificatePassword()),
resource,
null).get();
} else if (applicationCredentials.clientCertificate() != null) {
return context.acquireTokenByAuthorizationCode(
authorizationCode,
new URI(redirectUrl),
AsymmetricKeyCredential.create(
clientId(),
ApplicationTokenCredentials.privateKeyFromPem(new String(applicationCredentials.clientCertificate())),
ApplicationTokenCredentials.publicKeyFromPem(new String(applicationCredentials.clientCertificate()))),
resource,
null).get();
}
throw new AuthenticationException("Please provide either a non-null secret or a non-null certificate.");
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
} finally {
executor.shutdown();
}
}
private AuthenticationResult acquireAccessTokenFromRefreshToken(String resource, String refreshToken) throws IOException {
String authorityUrl = this.environment().activeDirectoryEndpoint() + this.domain();
ExecutorService executor = Executors.newSingleThreadExecutor();
AuthenticationContext context = new AuthenticationContext(authorityUrl, false, executor);
if (proxy() != null) {
context.setProxy(proxy());
}
try {
return context.acquireTokenByRefreshToken(refreshToken,
new ClientCredential(applicationCredentials.clientId(), applicationCredentials.clientSecret()),
resource, null).get();
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
} finally {
executor.shutdown();
}
}
Specifies the method that should be used to send the resulting token back to your app.
/**
* Specifies the method that should be used to send the resulting token back to your app.
*/
public enum ResponseMode {
the token is sent as a query parameter.
/**
* the token is sent as a query parameter.
*/
QUERY("query"),
the token is sent as part of a form data.
/**
* the token is sent as part of a form data.
*/
FORM_DATA("form_data");
private String value;
ResponseMode(String value) {
this.value = value;
}
}
}