Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.20.0
Current version - 1.20.1

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

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

```gradle
implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.20.0'
implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.20.1'
```

## Usage
Expand Down
4 changes: 4 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
Version 1.20.1
=============
- Fix Base64URL decoding bug (#938)

Version 1.20.0
=============
- Replace some usage of jackson-databind with azure-json (#918)
Expand Down
6 changes: 3 additions & 3 deletions msal4j-sdk/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.20.0
Current version - 1.20.1

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,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>msal4j</artifactId>
<version>1.20.0</version>
<version>1.20.1</version>
</dependency>
```
### Gradle

```gradle
compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.20.0'
compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.20.1'
```

## Usage
Expand Down
2 changes: 1 addition & 1 deletion msal4j-sdk/bnd.bnd
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Export-Package: com.microsoft.aad.msal4j;version="1.20.0"
Export-Package: com.microsoft.aad.msal4j;version="1.20.1"
Automatic-Module-Name: com.microsoft.aad.msal4j
2 changes: 1 addition & 1 deletion msal4j-sdk/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.20.0</version>
<version>1.20.1</version>
<packaging>jar</packaging>
<name>msal4j</name>
<description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static WebDriver createDefaultWebDriver() {
//No visual rendering, remove to see browser window when debugging
options.addArguments("--headless");
//Add to avoid issues if your real browser's history/cookies are affecting tests, should not be needed in ADO pipelines
//options.addArguments("--incognito");
options.addArguments("--incognito");

System.setProperty("webdriver.chrome.driver", "C:/Windows/chromedriver.exe");
ChromeDriver driver = new ChromeDriver(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

package com.microsoft.aad.msal4j;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Abstract class for an MSAL grant.
Expand All @@ -25,13 +29,12 @@ abstract class AbstractMsalAuthorizationGrant {
static final String SCOPE_PROFILE = "profile";
static final String SCOPE_OFFLINE_ACCESS = "offline_access";

static final String COMMON_SCOPES_PARAM = SCOPE_OPEN_ID + SCOPES_DELIMITER +
SCOPE_PROFILE + SCOPES_DELIMITER +
SCOPE_OFFLINE_ACCESS;
static final Set<String> COMMON_SCOPES = Stream.of(SCOPE_OPEN_ID, SCOPE_PROFILE, SCOPE_OFFLINE_ACCESS)
.collect(Collectors.toCollection(HashSet::new));

String scopes;
Set<String> scopes;

String getScopes() {
Set<String> getScopes() {
return scopes;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
package com.microsoft.aad.msal4j;

import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.oauth2.sdk.AuthorizationGrant;
import com.nimbusds.oauth2.sdk.ResourceOwnerPasswordCredentialsGrant;
import com.nimbusds.oauth2.sdk.SAML2BearerGrant;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

class AcquireTokenByAuthorizationGrantSupplier extends AuthenticationResultSupplier {

Expand Down Expand Up @@ -39,8 +39,7 @@ AuthenticationResult execute() throws Exception {
}

if (authGrant instanceof OAuthAuthorizationGrant) {
msalRequest.msalAuthorizationGrant =
processPasswordGrant((OAuthAuthorizationGrant) authGrant);
processPasswordGrant((OAuthAuthorizationGrant) authGrant);
}

if (authGrant instanceof IntegratedWindowsAuthorizationGrant) {
Expand Down Expand Up @@ -74,58 +73,52 @@ private boolean IsUiRequiredCacheSupported() {
clientApplication instanceof PublicClientApplication;
}

private OAuthAuthorizationGrant processPasswordGrant(
OAuthAuthorizationGrant authGrant) throws Exception {

if (!(authGrant.getAuthorizationGrant() instanceof ResourceOwnerPasswordCredentialsGrant)) {
return authGrant;
}
private void processPasswordGrant(OAuthAuthorizationGrant authGrant) throws Exception {

if (msalRequest.application().authenticationAuthority.authorityType != AuthorityType.AAD) {
return authGrant;
//Additional processing is only needed if it's a password grant with an AAD authority
if (!(authGrant.getParamValue(GrantConstants.GRANT_TYPE_PARAMETER).equals(GrantConstants.PASSWORD))
|| msalRequest.application().authenticationAuthority.authorityType != AuthorityType.AAD) {
return;
}

ResourceOwnerPasswordCredentialsGrant grant =
(ResourceOwnerPasswordCredentialsGrant) authGrant.getAuthorizationGrant();

UserDiscoveryResponse userDiscoveryResponse = UserDiscoveryRequest.execute(
this.clientApplication.authenticationAuthority.getUserRealmEndpoint(grant.getUsername()),
this.clientApplication.authenticationAuthority.getUserRealmEndpoint(authGrant.getParamValue(GrantConstants.USERNAME_PARAMETER)),
msalRequest.headers().getReadonlyHeaderMap(),
msalRequest.requestContext(),
this.clientApplication.serviceBundle());

if (userDiscoveryResponse.isAccountFederated()) {
WSTrustResponse response = WSTrustRequest.execute(
userDiscoveryResponse.federationMetadataUrl(),
grant.getUsername(),
grant.getPassword().getValue(),
authGrant.getParamValue(GrantConstants.USERNAME_PARAMETER),
authGrant.getParamValue(GrantConstants.PASSWORD_PARAMETER),
userDiscoveryResponse.cloudAudienceUrn(),
msalRequest.requestContext(),
this.clientApplication.serviceBundle(),
this.clientApplication.logPii());

AuthorizationGrant updatedGrant = getSAMLAuthorizationGrant(response);

authGrant = new OAuthAuthorizationGrant(updatedGrant, authGrant.getParameters());
authGrant.addAndReplaceParams(getSAMLAuthGrantParameters(response));
}
return authGrant;
}

private AuthorizationGrant getSAMLAuthorizationGrant(WSTrustResponse response) throws UnsupportedEncodingException {
AuthorizationGrant updatedGrant;
private Map<String, List<String>> getSAMLAuthGrantParameters(WSTrustResponse response) {
Map<String, List<String>> params = new LinkedHashMap<>();

if (response.isTokenSaml2()) {
updatedGrant = new SAML2BearerGrant(new Base64URL(
Base64.getEncoder().encodeToString(response.getToken().getBytes(StandardCharsets.UTF_8))));
params.put(GrantConstants.GRANT_TYPE_PARAMETER, Collections.singletonList(GrantConstants.SAML_2_BEARER));
} else {
updatedGrant = new SAML11BearerGrant(new Base64URL(
Base64.getEncoder().encodeToString(response.getToken()
.getBytes(StandardCharsets.UTF_8))));
params.put(GrantConstants.GRANT_TYPE_PARAMETER, Collections.singletonList(GrantConstants.SAML_1_1_BEARER));
}
return updatedGrant;

params.put(GrantConstants.ASSERTION_PARAMETER, Collections.singletonList(new Base64URL(
Base64.getEncoder().encodeToString(response.getToken()
.getBytes(StandardCharsets.UTF_8))).toString()));

return params;
}

private AuthorizationGrant getAuthorizationGrantIntegrated(String userName) throws Exception {
AuthorizationGrant updatedGrant;
private Map<String, List<String>> getAuthorizationGrantIntegrated(String userName) throws Exception {
Map<String, List<String>> params;

String userRealmEndpoint = this.clientApplication.authenticationAuthority.
getUserRealmEndpoint(URLEncoder.encode(userName, StandardCharsets.UTF_8.name()));
Expand All @@ -152,7 +145,7 @@ private AuthorizationGrant getAuthorizationGrantIntegrated(String userName) thro
this.clientApplication.serviceBundle(),
this.clientApplication.logPii());

updatedGrant = getSAMLAuthorizationGrant(wsTrustResponse);
params = getSAMLAuthGrantParameters(wsTrustResponse);
} else if (userRealmResponse.isAccountManaged()) {
throw new MsalClientException(
"Password is required for managed user",
Expand All @@ -163,6 +156,6 @@ private AuthorizationGrant getAuthorizationGrantIntegrated(String userName) thro
AuthenticationErrorCode.USER_REALM_DISCOVERY_FAILED);
}

return updatedGrant;
return params;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

package com.microsoft.aad.msal4j;

import com.nimbusds.oauth2.sdk.AuthorizationCode;
import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
import com.nimbusds.oauth2.sdk.AuthorizationGrant;
import com.nimbusds.oauth2.sdk.pkce.CodeVerifier;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

class AuthorizationCodeRequest extends MsalRequest {

Expand All @@ -17,19 +17,19 @@ class AuthorizationCodeRequest extends MsalRequest {
}

private static AbstractMsalAuthorizationGrant createMsalGrant(AuthorizationCodeParameters parameters) {
Map<String, List<String>> params = new LinkedHashMap<>();

params.put(GrantConstants.GRANT_TYPE_PARAMETER, Collections.singletonList(GrantConstants.AUTHORIZATION_CODE));
params.put("code", Collections.singletonList(parameters.authorizationCode()));

if (parameters.redirectUri() != null) {
params.put("redirect_uri", Collections.singletonList(parameters.redirectUri().toString()));
}

AuthorizationGrant authorizationGrant;
if (parameters.codeVerifier() != null) {
authorizationGrant = new AuthorizationCodeGrant(
new AuthorizationCode(parameters.authorizationCode()),
parameters.redirectUri(),
new CodeVerifier(parameters.codeVerifier()));

} else {
authorizationGrant = new AuthorizationCodeGrant(
new AuthorizationCode(parameters.authorizationCode()), parameters.redirectUri());
params.put("code_verifier", Collections.singletonList(parameters.codeVerifier()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how much of PKCE was handled by nimbus, so please make sure we have enough tests that validate that auth_code uses PKCE. Both desktop and web app scenarios.

}

return new OAuthAuthorizationGrant(authorizationGrant, parameters.scopes(), parameters.claims());
return new OAuthAuthorizationGrant(params, parameters.scopes(), parameters.claims());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ private AuthorizationRequestUrlParameters(Builder builder) {
requestParameters.put("redirect_uri", Collections.singletonList(this.redirectUri));
this.scopes = builder.scopes;

String[] commonScopes = AbstractMsalAuthorizationGrant.COMMON_SCOPES_PARAM.split(" ");

Set<String> scopesParam = new LinkedHashSet<>(Arrays.asList(commonScopes));
Set<String> scopesParam = new LinkedHashSet<>(AbstractMsalAuthorizationGrant.COMMON_SCOPES);

scopesParam.addAll(builder.scopes);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

package com.microsoft.aad.msal4j;

import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

Expand All @@ -16,14 +18,6 @@ class ClientCredentialRequest extends MsalRequest {
useful to applications in general because the token provider must implement all authentication logic. */
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,
Expand All @@ -34,6 +28,10 @@ class ClientCredentialRequest extends MsalRequest {
}

private static OAuthAuthorizationGrant createMsalGrant(ClientCredentialParameters parameters) {
return new OAuthAuthorizationGrant(new ClientCredentialsGrant(), parameters.scopes(), parameters.claims());
Map<String, List<String>> params = new LinkedHashMap<>();

params.put(GrantConstants.GRANT_TYPE_PARAMETER, Collections.singletonList(GrantConstants.CLIENT_CREDENTIALS));

return new OAuthAuthorizationGrant(params, parameters.scopes(), parameters.claims());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
*/
class DefaultHttpClientManagedIdentity extends DefaultHttpClient {

// CodeQL [SM03767] False positive: in addTrustedCertificateThumbprint() we create a TrustManager that only trusts a certificate with a specific thumbprint.
public static final HostnameVerifier ALL_HOSTS_ACCEPT_HOSTNAME_VERIFIER = new HostnameVerifier() {
@SuppressWarnings("BadHostnameVerifier")
@Override
Expand Down Expand Up @@ -85,6 +84,8 @@ public static void addTrustedCertificateThumbprint(HttpsURLConnection httpsUrlCo
String certificateThumbprint) {
//We expect the connection to work against a specific server side certificate only, so it's safe to disable the
// host name verification.

// CodeQL [SM03767] False positive: the TrustManager created later on will only trust a certificate with a specific thumbprint.
if (httpsUrlConnection.getHostnameVerifier() != ALL_HOSTS_ACCEPT_HOSTNAME_VERIFIER) {
httpsUrlConnection.setHostnameVerifier(ALL_HOSTS_ACCEPT_HOSTNAME_VERIFIER);
}
Expand Down
Loading
Loading