Skip to content

Commit

Permalink
Merge pull request #520 from AzureAD/dev
Browse files Browse the repository at this point in the history
merge to main
  • Loading branch information
siddhijain authored Jun 29, 2022
2 parents 5516914 + 168d2b4 commit b78eb6e
Show file tree
Hide file tree
Showing 17 changed files with 318 additions and 23 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Quick links:
The library supports the following Java environments:
- Java 8 (or higher)

Current version - 1.12.0
Current version - 1.13.0

You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/master/changelog.txt).

Expand All @@ -28,12 +28,12 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.12.0</version>
<version>1.13.0</version>
</dependency>
```
### Gradle

compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.12.0'
compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.13.0'

## Usage

Expand Down
6 changes: 6 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version 1.13.0
=============
- Provide token caching functionality for managed identity tokens
- Updates for obo-for-service-principal scenarios
- version updates for nimbusds-oauth2 library

Version 1.12.0
=============
- Updates several dependencies to avoid security vulnerabilities
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.12.0</version>
<version>1.13.0</version>
<packaging>jar</packaging>
<name>msal4j</name>
<description>
Expand Down Expand Up @@ -36,7 +36,7 @@
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<version>9.32</version>
<version>9.35</version>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@
import java.security.*;
import java.security.cert.CertificateException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.function.Function;

import static com.microsoft.aad.msal4j.TestConstants.KEYVAULT_DEFAULT_SCOPE;
import static org.easymock.EasyMock.*;
import static org.testng.Assert.*;

Expand Down Expand Up @@ -277,12 +280,10 @@ private ClientCredentialRequest getClientCredentialRequest(ConfidentialClientApp
PublicApi.ACQUIRE_TOKEN_FOR_CLIENT,
clientCredentials);

ClientCredentialRequest clientCredentialRequest =
new ClientCredentialRequest(
return new ClientCredentialRequest(
clientCredentials,
app,
requestContext);
return clientCredentialRequest;
}

@Test(expectedExceptions = MsalClientException.class)
Expand All @@ -298,6 +299,82 @@ public void testClientAssertion_throwsException() throws Exception{

}

@Test
public void validateAppTokenProviderAsync() throws Exception{

SignedJWT jwt = createClientAssertion("issuer");

ClientAssertion clientAssertion = new ClientAssertion(jwt.serialize());

IClientCredential iClientCredential = ClientCredentialFactory.createFromClientAssertion(
clientAssertion.assertion());

//builds client with AppTokenProvider
ConfidentialClientApplication cca = ConfidentialClientApplication.
builder(TestConfiguration.AAD_CLIENT_ID, iClientCredential)
.appTokenProvider((parameters) -> {
Assert.assertNotNull(parameters.scopes);
Assert.assertNotNull(parameters.correlationId);
Assert.assertNotNull(parameters.tenantId);
return getAppTokenProviderResult("/default");
})
.build();

IAuthenticationResult result1 = cca.acquireToken(ClientCredentialParameters
.builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE))
.tenant("tenant1")
.build())
.get();

Assert.assertNotNull(result1.accessToken());

Assert.assertEquals(cca.tokenCache.accessTokens.size(), 1);

//Acquire token from cache

IAuthenticationResult result2 = cca.acquireToken(ClientCredentialParameters
.builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE))
.build())
.get();

Assert.assertEquals(result1.accessToken(), result2.accessToken());

Assert.assertEquals(cca.tokenCache.accessTokens.size(), 1);

cca = ConfidentialClientApplication.
builder(TestConfiguration.AAD_CLIENT_ID, iClientCredential)
.appTokenProvider((parameters) -> {
Assert.assertNotNull(parameters.scopes);
Assert.assertNotNull(parameters.correlationId);
Assert.assertNotNull(parameters.tenantId);
return getAppTokenProviderResult("/newScope");
})
.build();

IAuthenticationResult result3 = cca.acquireToken(ClientCredentialParameters
.builder(Collections.singleton("/newScope"))
.tenant("tenant1")
// .claims(new ClaimsRequest().formatAsClaimsRequest(TestConstants.CLAIMS))
.build())
.get();

Assert.assertNotEquals(result2.accessToken(), result3.accessToken());
Assert.assertEquals(cca.tokenCache.accessTokens.size(), 1);

}

private CompletableFuture<TokenProviderResult> getAppTokenProviderResult(String differentScopesForAt)
{
long currTimestampSec = new Date().getTime() / 1000;
TokenProviderResult token = new TokenProviderResult();
token.setAccessToken(TestConstants.DEFAULT_ACCESS_TOKEN + differentScopesForAt); //Used to indicate that there is a new access token for a different set of scopes
token.setTenantId("tenantId");
token.setExpiresInSeconds(currTimestampSec + 1000000);
token.setRefreshInSeconds(currTimestampSec + 800000);

return CompletableFuture.completedFuture(token);
}

private SignedJWT createClientAssertion(String issuer) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, NoSuchProviderException, JOSEException {
IClientCertificate certificate = CertificateHelper.getClientCertificate();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ public class TestConstants {
public final static String AUTHORITY_ARLINGTON = "https://login.microsoftonline.us/" + ARLINGTON_AUTHORITY_TENANT;
public final static String AUTHORITY_MOONCAKE = "https://login.chinacloudapi.cn/mncmsidlab1.partner.onmschina.cn";
public final static String AUTHORITY_PUBLIC_TENANT_SPECIFIC = "https://login.microsoftonline.com/" + MICROSOFT_AUTHORITY_TENANT;

public final static String DEFAULT_ACCESS_TOKEN = "defaultAccessToken";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.aad.msal4j;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

class AcquireTokenByAppProviderSupplier extends AuthenticationResultSupplier {

private AppTokenProviderParameters appTokenProviderParameters;

private ClientCredentialRequest clientCredentialRequest;

AcquireTokenByAppProviderSupplier(AbstractClientApplicationBase clientApplication,
ClientCredentialRequest clientCredentialRequest,
AppTokenProviderParameters appTokenProviderParameters) {
super(clientApplication, clientCredentialRequest);
this.clientCredentialRequest = clientCredentialRequest;
this.appTokenProviderParameters = appTokenProviderParameters;
}

private static void validateTokenProviderResult(TokenProviderResult tokenProviderResult) {
if (null == tokenProviderResult.getAccessToken() || tokenProviderResult.getAccessToken().isEmpty()) {
handleInvalidExternalValueError(tokenProviderResult.getAccessToken());
}

if (tokenProviderResult.getExpiresInSeconds() == 0 || tokenProviderResult.getExpiresInSeconds() < 0) {
handleInvalidExternalValueError(Long.valueOf(tokenProviderResult.getExpiresInSeconds()).toString());
}

if (null == tokenProviderResult.getTenantId() || tokenProviderResult.getTenantId().isEmpty()) {
handleInvalidExternalValueError(tokenProviderResult.getTenantId());
}
}

private static void handleInvalidExternalValueError(String nameOfValue) {
throw new MsalClientException("The following token provider result value is invalid" + nameOfValue, "Invalid_TokenProviderResult_Input");
}

@Override
AuthenticationResult execute() throws Exception {

AuthenticationResult authenticationResult = fetchTokenUsingAppTokenProvider(appTokenProviderParameters);

TokenRequestExecutor tokenRequestExecutor = new TokenRequestExecutor(
clientCredentialRequest.application().authenticationAuthority,
msalRequest,
clientApplication.getServiceBundle()
);

clientApplication.tokenCache.saveTokens(tokenRequestExecutor, authenticationResult, clientCredentialRequest.application().authenticationAuthority.host);

return authenticationResult;
}

public AuthenticationResult fetchTokenUsingAppTokenProvider(AppTokenProviderParameters appTokenProviderParameters) throws ExecutionException, InterruptedException {

CompletableFuture<TokenProviderResult> completableFuture = this.clientCredentialRequest.appTokenProvider.apply(appTokenProviderParameters);

TokenProviderResult tokenProviderResult = completableFuture.get();

validateTokenProviderResult(tokenProviderResult);

return AuthenticationResult.builder()
.accessToken(tokenProviderResult.getAccessToken())
.refreshToken(null)
.idToken(null)
.expiresOn(tokenProviderResult.getExpiresInSeconds())
.refreshOn(tokenProviderResult.getRefreshInSeconds())
.build();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class AcquireTokenByClientCredentialSupplier extends AuthenticationResultSupplier {

private final static Logger LOG = LoggerFactory.getLogger(AcquireTokenByClientCredentialSupplier.class);
private static final Logger LOG = LoggerFactory.getLogger(AcquireTokenByClientCredentialSupplier.class);
private ClientCredentialRequest clientCredentialRequest;

AcquireTokenByClientCredentialSupplier(ConfidentialClientApplication clientApplication,
Expand Down Expand Up @@ -55,11 +55,36 @@ AuthenticationResult execute() throws Exception {
}

private AuthenticationResult acquireTokenByClientCredential() throws Exception {

if (this.clientCredentialRequest.appTokenProvider != null) {

String claims = "";
if (null != clientCredentialRequest.parameters.claims()) {
claims = clientCredentialRequest.parameters.claims().toString();
}

AppTokenProviderParameters appTokenProviderParameters = new AppTokenProviderParameters(
clientCredentialRequest.parameters.scopes(),
clientCredentialRequest.requestContext().correlationId(),
claims,
clientCredentialRequest.parameters.tenant()
);

AcquireTokenByAppProviderSupplier supplier =
new AcquireTokenByAppProviderSupplier(this.clientApplication,
clientCredentialRequest,
appTokenProviderParameters);

return supplier.execute();
}

AcquireTokenByAuthorizationGrantSupplier supplier = new AcquireTokenByAuthorizationGrantSupplier(
this.clientApplication,
clientCredentialRequest,
null);

return supplier.execute();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@

package com.microsoft.aad.msal4j;


import lombok.extern.slf4j.Slf4j;

import java.net.URL;
import java.util.Date;

@Slf4j
class AcquireTokenSilentSupplier extends AuthenticationResultSupplier {

private SilentRequest silentRequest;
Expand Down Expand Up @@ -105,6 +109,8 @@ AuthenticationResult execute() throws Exception {
throw new MsalClientException(AuthenticationErrorMessage.NO_TOKEN_IN_CACHE, AuthenticationErrorCode.CACHE_MISS);
}

log.info("Returning token from cache");

return res;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.aad.msal4j;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

import java.util.Set;

@Getter
@Setter
@AllArgsConstructor
/// The authentication parameters provided to the app token provider callback.
public class AppTokenProviderParameters {

/// Specifies which scopes to request.
public Set<String> scopes;
/// Correlation id of the authentication request.
public String correlationId;
/// A string with one or multiple claims.
public String claims;
/// tenant id
public String tenantId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,11 @@ private void send302Response(HttpExchange httpExchange, String redirectUri) thro
}

private void send200Response(HttpExchange httpExchange, String response) throws IOException {
httpExchange.sendResponseHeaders(200, response.length());
byte[] responseBytes = response.getBytes("UTF-8");
httpExchange.getResponseHeaders().set("Content-Type", "text/html; charset=UTF-8");
httpExchange.sendResponseHeaders(200, responseBytes.length);
OutputStream os = httpExchange.getResponseBody();
os.write(response.getBytes("UTF-8"));
os.write(responseBytes);
os.close();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,29 @@

import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;

import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

class ClientCredentialRequest extends MsalRequest {

ClientCredentialParameters parameters;
Function<AppTokenProviderParameters, CompletableFuture<TokenProviderResult>> appTokenProvider;

ClientCredentialRequest(ClientCredentialParameters parameters,
ConfidentialClientApplication application,
RequestContext requestContext) {
super(application, createMsalGrant(parameters), requestContext);
this.parameters = parameters;
appTokenProvider = null;
}

ClientCredentialRequest(ClientCredentialParameters parameters,
ConfidentialClientApplication application,
RequestContext requestContext,
Function<AppTokenProviderParameters, CompletableFuture<TokenProviderResult>> appTokenProvider) {
super(application, createMsalGrant(parameters), requestContext);
this.parameters = parameters;
this.appTokenProvider = appTokenProvider;
}

private static OAuthAuthorizationGrant createMsalGrant(ClientCredentialParameters parameters) {
Expand Down
Loading

0 comments on commit b78eb6e

Please sign in to comment.