Hot questions for Using Azure in active directory

Top Java Programmings / Azure / active directory

Question:

I'm building an application that needs access to our clients' Office 365 Management Activities. I've followed the steps outlined in this Azure Active Directory overview, and am able to use the OAuth code to acquire an initial Access Token, as well as use this token to set up O365 subscriptions.

However, when I use the refresh_token provided with my initial token to acquire a new Access Token, I get the following error:

{"error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID '8f72f805-dfd2-428d-8b0e-771a98d26c16'. Send an interactive authorization request for this user and resource.\r\nTrace ID: df229c3f-8f28-420b-9ac3-321ab1b2ad09\r\nCorrelation ID: 0e0f2bcb-4b19-458a-8556-2a6d4e51379f\r\nTimestamp: 2016-10-03 17:33:20Z","error":"invalid_grant"}

Since I'm able to acquire and use the initial Access Token, I'm pretty sure that the user is granting my applications some permissions. Is there a specific permission that I need in order to acquire a new Access Token using the Refresh Token?

Edit: Specifically, I'm using the com.microsoft.azure::adal4j java package, AuthenticationContext class, acquireTokenByAuthorizationCode and acquireTokenByRefreshToken methods:

public class AzureProvisioner {
    private final AuthenticationContext authService = new AuthenticationContext(
            "https://login.windows.net/common/oauth2/token", true, Executors.newSingleThreadExecutor());
    private final ClientCredential clientCredential = new ClientCredential("azureAppId", "azureAppSecret");
    public static final String resource = "https://manage.office.com";
    // Internal implementation of REST interface; Microsoft didn't provide a Java Library
    final Office365ManagementApi managementApi;

    public void acquireToken(final String authCode, final URI redirectUri) {
        final AuthenticationResult authResult = authService.acquireTokenByAuthorizationCode(
                authCode, redirectUri, clientCredential, resource, null).get()
        // internal library code, gets the "tid" field from parsing the JWT token
        final String tenantId = JwtAccessToken.fromToken(authResult.getAccessToken()).getTid();

        // works
        createInitialSubscription(customerId, authResult.getAccessToken(), tenantId);

        // throws an error
        final AuthenticationResult refreshResult = authService.acquireTokenByRefreshToken(
                authResult.getRefreshToken(), clientCredential, null).get();
    }

    private void createInitialSubscription(final String accessToken, final String tenantId) {
        final String authHeader = "Authorization: Bearer " + accessToken;
        final String contentType = "Audit.AzureActiveDirectory";
        // internal implementation
        final CreateWebhookRequest requestBody = new CreateWebhookRequest();
        managementApi.createSubscription(authHeader, tenantId, contentType, requestBody);
    }
}

The same code, without any external dependencies, also does not work for me:

public class AzureProvisioner {
    private final AuthenticationContext authService = new AuthenticationContext(
            "https://login.windows.net/common/oauth2/token", true, Executors.newSingleThreadExecutor());
    private final ClientCredential clientCredential = new ClientCredential("8f72f805-dfd2-428d-8b0e-771a98d26c16", "secret");
    public final String resource = "https://manage.office.com";
    private URI redirectUri = new URI("https://localhost");

    private static final String oAuthUrl = "https://login.windows.net/common/oauth2/authorize?response_type=code&client_id=8f72f805-dfd2-428d-8b0e-771a98d26c16&resource=https%3A%2F%2Fmanage.office.com&redirect_uri=https%3A%2F%2Flocalhost";

    public AzureProvisioner() throws Exception {
        // do nothing
    }

    public static void main(String... args) throws Exception {
        final String authCode = "AQABAAAAAADRNYRQ3dhRSrm...";
        new AzureProvisioner().acquireToken(authCode);
    }

    public void acquireToken(final String authCode) throws Exception {
        final AuthenticationResult authResult = authService.acquireTokenByAuthorizationCode(
                authCode, redirectUri, clientCredential, resource, null).get();
        System.out.println(authResult.getAccessToken());

        // throws an error
        final AuthenticationResult refreshResult = authService.acquireTokenByRefreshToken(
                authResult.getRefreshToken(), clientCredential, resource, null).get();
        System.out.println(refreshResult.getAccessToken());
    }
}

Using a proxy, I took a trace of the https refresh request:

Method: POST
Protocol-Version: HTTP/1.1
Protocol: https
Host: login.windows.net
File: /common/oauth2/token
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 876

refresh_token={token}
&resource=https%3A%2F%2Fmanage.office.com
&grant_type=refresh_token
&scope=openid
&client_secret={secret}
&client_id=8f72f805-dfd2-428d-8b0e-771a98d26c16

Answer:

It turns out that the root issue was with my application permissions. Under My Application > Settings > Required Permissions > Office 365 Management APIs, I had selected the "Application Permissions", where I needed to select the "Delegated Permissions". Swapping those over, my code immediately started working as expected.

Question:

I am struggling to validate an Azure AD token signature.

When I look up the correct key description in the "jwks_uri" field under

https://login.microsoftonline.com/common/.well-known/openid-configuration

I check the belonging key data .

I try to use the "n" - modulus and "e" fields to generate the public key for the signature validation I end up with an error:

BASE64Decoder decoder = new BASE64Decoder();        
byte[] modulusBytes = decoder.decodeBuffer(n);
byte[] exponentBytes = decoder.decodeBuffer(e);

BigInteger modulusInt = new BigInteger(1, modulusBytes);
BigInteger exponentInt = new BigInteger(1, exponentBytes);

try {
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(modulusInt, exponentInt);
RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(publicSpec);
Jwt<Header, String> c = Jwts.parser().setSigningKey(pubKey).parsePlaintextJwt(token);

} catch (Exception ex) {
  ex.printStackTrace();
}

Console:

io.jsonwebtoken.SignatureException: Unable to verify RSA signature using configured PublicKey. Signature length not correct: got 256 but was expecting 246
at io.jsonwebtoken.impl.crypto.RsaSignatureValidator.isValid(RsaSignatureValidator.java:50)
at io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator.isValid(DefaultJwtSignatureValidator.java:47)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:351)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
at io.jsonwebtoken.impl.DefaultJwtParser.parsePlaintextJwt(DefaultJwtParser.java:503)
at com.ge.hc.pfh.poc.ams.filter.JwtFilter.doFilter(JwtFilter.java:120)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at com.ge.hc.pfh.poc.ams.filter.ApiOriginFIlter.doFilter(ApiOriginFIlter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at com.ge.hc.pfh.poc.ams.filter.MDCFilter.doFilter(MDCFilter.java:34)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

I tried another approach to use the "x5c" filed which is a base 64 encoded cert chain:

byte[] certChain = Base64.getDecoder().decode(x5c);
X509Certificate cert = X509CertUtils.parse(certChain);
PublicKey pubKeyNew = cert.getPublicKey();
Claims claims3 = Jwts.parser()
          .setSigningKey(pubKeyNew)
            .parseClaimsJws(token).getBody();

I end up with an other error:

io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:354)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
at io.jsonwebtoken.impl.DefaultJwtParser.parsePlaintextJwt(DefaultJwtParser.java:503)
at com.ge.hc.pfh.poc.ams.filter.JwtFilter.doFilter(JwtFilter.java:106)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at com.ge.hc.pfh.poc.ams.filter.ApiOriginFIlter.doFilter(ApiOriginFIlter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at com.ge.hc.pfh.poc.ams.filter.MDCFilter.doFilter(MDCFilter.java:34)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

Would anyone know what I am doing wrong? Thanks.


Answer:

First example

Modulus and Exponent (n and e) in https://login.microsoftonline.com/common/discovery/keys are encoded in base64url and not in base64, so the code to decode them should be

byte[] modulusBytes = Base64.getUrlDecoder().decode(n);
BigInteger modulusInt = new BigInteger(1, modulusBytes);

Do not use old com.sun.misc.BASE64Decoder

If the JWT is signed you should not use JWTParser.plaintextJwt(). According to documentation

plaintextJwt: a compact serialized unsigned plaintext JWT string

Use instead parseClaimsJws or parsePlaintextJws. The second method only if the payload is a string non-JSON

Second example

The second example is basically right. I assume X509CertUtils.parse(certChain) is similar to

 InputStream in = new ByteArrayInputStream(certChain);
 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
 X509Certificate cert = (X509Certificate)certFactory.generateCertificate(in);

Modulus and exponent of the certificate are the same that the decoded, so public key is equivalent

There are two similar certificates in the link, check both. You should be able to validate the signature. If not, then the token is not signed with those keys

Question:

I have an azure subscription, an AD instance with admin rights. Is it possible to access the AD via Graph API and get a list of all users ?

Note: I have already researched and came across this sample example : https://github.com/AzureADSamples/WebApp-GraphAPI-Java

But this requires me to create a Java Web-App, add it to application on azure, get a client ID and secret and then authenticate.

Is there no way to authenticate (get an access token) by writing a simple command line java program using the ADAl4J library and provide AD username and password as credentials ?

For example :

 public static void main(String srgs[])
    {
        String authority = "https://login.windows.net/";
        String tenant = "mytenant.onmicrosoft.com" ;


        AuthenticationContext context = null;
        AuthenticationResult result = null;
        ExecutorService service = null;
        try {
            service = Executors.newFixedThreadPool(1);
            context = new AuthenticationContext(authority + tenant + "/", true,
                    service);
            Future<AuthenticationResult> future = context.acquireToken(
                    "https://graph.windows.net", new ClientCredential("AD-USERNAME",
                    "AD-PASSWORD"), null);
            result = future.get();
        } catch (Exception e) {
             e.getCause();
        } finally {
            service.shutdown();
        }


    }

Answer:

It is indeed possible. AzureAD supports the OAuth Resource Owner Password Credential Grant. The ADAL SDK has recently added support for it (https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/2.8.10804.1442-rc).

Note however that this method of auth fails when multi-factor auth is enabled for the account, or if the account is configured for federated auth to an IdP other than ADFS.

Vittorio has a good blog post about it here:

http://www.cloudidentity.com/blog/2014/07/08/using-adal-net-to-authenticate-users-via-usernamepassword/

Question:

I am trying to find an example of Azure Adal (Oauth2) usage with Dropwizard, but can't seem to find anything.

The Oauth2 example on dropwizard's website is really vague, I can't seem to understand where I would provide my ClientId, and token endpoints.

Anyone have an example on how to use Oauth2 (Azure AD) with Dropwizard


Answer:

I have no experience for Dropwizard, but I think you might want to integrate Azure AD with your application for calling some Azure services or implementing authentication like SSO.

I searched the user manual for OAuth2 with Dropwizard. It seems to do the authentication and authorization via register the related REST service into the application container as filter likely for Spring Framework.

There is a sample on Azure offical site shown how to integrate Azure AD into a Java web application. I think it's helpful for you to know the steps.

Any concern, please feel free to let me know.

Question:

In attempting to authenticate with Active Directory to retrieve an access token, I followed the following steps:

1 - Created an application in Azure using the following directions (step 3): https://github.com/Azure-Samples/active-directory-java-native-headless#step-3--register-the-sample-with-your-azure-active-directory-tenant

2 - Created a "Azure Active Directory" user account with the "Data Factory Contributor" permission.

3 - Used the steps from (url below), I followed the example code to attempt to retrieve an access token: https://github.com/Azure-Samples/active-directory-java-native-headless/blob/master/src/main/java/PublicClient.java

Note: the resource I am using is... https://management.core.windows.net/

... using the following as an example: https://docs.microsoft.com/en-us/azure/data-factory/quickstart-create-data-factory-rest-api#authenticate-with-azure-ad

4 - Server is responding with the following error:

Exception in thread "main" java.util.concurrent.ExecutionException:
com.microsoft.aad.adal4j.AuthenticationException:
 {
    "error_description": "AADSTS65001: The user or administrator has not
    consented to use the application with ID '<my-app-id>' named
    '<my-app-name>'. Send an interactive authorization request for this
    user and resource.\r\nTrace ID: d0af56e6-aaa3-4d25-b23b-
    2984ed2b4400\r\nCorrelation ID: 2422cc2f-1cdd-45c5-8b7c-
    46b1eee4ffae\r\nTimestamp: 2019-03-22 04:58:16Z",

    "error": "invalid_grant"
 }

What grants are required in order to get the authentication to work?


Answer:

You should make an authorization request to Azure AD that includes the parameter prompt=admin_consent.

Go to a URL such as https://login.microsoftonline.com/tenant-id/oauth2/authorize?client_id=app-client-id&redirect_uri=encoded-reply-url&response_type=code&prompt=admin_consent.

Use your admin account to consent the permissions.

Then we can get the access token successfully.

Question:

I'm trying to build azure AD library for java, but I'm getting this error when I build, any idea why?

2218 [pool-1-thread-1] ERROR com.microsoft.aad.adal4j.AuthenticationContext - [Correlation ID: ac256c7c-b9ae-41ff-b39c-f0746a2275f7] Request to acquire token failed.
java.lang.ClassCastException: sun.net.www.protocol.https.HttpsURLConnectionImpl cannot be cast to javax.net.ssl.HttpsURLConnection
at com.microsoft.aad.adal4j.HttpHelper.openConnection(HttpHelper.java:102)
at com.microsoft.aad.adal4j.HttpHelper.openConnection(HttpHelper.java:115)
at com.microsoft.aad.adal4j.HttpHelper.executeHttpGet(HttpHelper.java:49)
at com.microsoft.aad.adal4j.AuthenticationAuthority.doDynamicInstanceDiscovery(AuthenticationAuthority.java:146)
at com.microsoft.aad.adal4j.AuthenticationAuthority.doInstanceDiscovery(AuthenticationAuthority.java:130)
at com.microsoft.aad.adal4j.AuthenticationContext.acquireTokenCommon(AuthenticationContext.java:813)
at com.microsoft.aad.adal4j.AuthenticationContext.access$1(AuthenticationContext.java:806)
at com.microsoft.aad.adal4j.AuthenticationContext$1.call(AuthenticationContext.java:175)
at com.microsoft.aad.adal4j.AuthenticationContext$1.call(AuthenticationContext.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Warning: Could not get charToByteConverterClass!
638 [main] INFO com.microsoft.aad.adal4j.WSTrustResponse - Found token of type: urn:oasis:names:tc:SAML:1.0:assertion
EDIT:

java -version output is :

java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

mvn -v output is :

Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-10T18:41:47+02:00)
Maven home: /Users/vanddel/work/apache-maven-3.3.9
Java version: 1.8.0_91, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.11.4", arch: "x86_64", family: "mac"

This was the output of mvn package, I edited the pom.xml file to add slf4j-simple dependency, and it fixed the NOP error in the output, but then I got the ClassCastException error.

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
</dependency>




MacBook:azure-activedirectory-library-for-java vanddel$ mvn package
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building adal4j 1.1.2
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ adal4j ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.0:compile (default-compile) @ adal4j ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ adal4j ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 5 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.0:testCompile (default-testCompile) @ adal4j ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 16 source files to /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.10:test (default-test) @ adal4j ---
[INFO] Surefire report directory: /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running TestSuite
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Warning: Could not get charToByteConverterClass!
Tests run: 65, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.032 sec

Results :

Tests run: 65, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- maven-jar-plugin:2.5:jar (default-jar) @ adal4j ---
[INFO] 
[INFO] --- maven-javadoc-plugin:2.9.1:jar (attach-javadocs) @ adal4j ---
[INFO] 
Loading source files for package com.microsoft.aad.adal4j...
Constructing Javadoc information...
Standard Doclet version 1.8.0_91
Building tree for all the packages and classes...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/AsymmetricKeyCredential.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/AuthenticationCallback.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/AuthenticationContext.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/AuthenticationException.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/AuthenticationResult.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/ClientAssertion.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/ClientCredential.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/NamespaceContextImpl.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/UserInfo.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/package-frame.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/package-summary.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/package-tree.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/constant-values.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/serialized-form.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/UserInfo.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/NamespaceContextImpl.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/ClientCredential.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/ClientAssertion.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/AuthenticationResult.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/AuthenticationException.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/AuthenticationContext.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/AuthenticationCallback.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/class-use/AsymmetricKeyCredential.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/com/microsoft/aad/adal4j/package-use.html...
Building index for all the packages and classes...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/overview-tree.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/index-all.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/deprecated-list.html...
Building index for all classes...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/allclasses-frame.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/allclasses-noframe.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/index.html...
Generating /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/apidocs/help-doc.html...
14 warnings
[WARNING] Javadoc Warnings
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:95: warning: no description for @throws
[WARNING] * @throws CertificateEncodingException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:96: warning: no description for @throws
[WARNING] * @throws NoSuchAlgorithmException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:108: warning: no description for @throws
[WARNING] * @throws CertificateEncodingException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:109: warning: no description for @throws
[WARNING] * @throws NoSuchAlgorithmException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:136: warning: no description for @throws
[WARNING] * @throws KeyStoreException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:137: warning: no description for @throws
[WARNING] * @throws NoSuchProviderException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:138: warning: no description for @throws
[WARNING] * @throws NoSuchAlgorithmException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:139: warning: no description for @throws
[WARNING] * @throws CertificateException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:140: warning: no description for @throws
[WARNING] * @throws FileNotFoundException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:141: warning: no description for @throws
[WARNING] * @throws IOException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AsymmetricKeyCredential.java:142: warning: no description for @throws
[WARNING] * @throws UnrecoverableKeyException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AuthenticationContext.java:145: warning: no description for @param
[WARNING] * @param sslSocketFactory
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AuthenticationContext.java:310: warning: no description for @throws
[WARNING] * @throws AuthenticationException
[WARNING] ^
[WARNING] /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/src/main/java/com/microsoft/aad/adal4j/AuthenticationContext.java:376: warning: no description for @throws
[WARNING] * @throws AuthenticationException
[WARNING] ^
[INFO] Building jar: /Users/vanddel/Documents/workspace/azure-activedirectory-library-for-java/target/adal4j-1.1.2-javadoc.jar
[INFO] 
[INFO] >>> maven-source-plugin:2.2.1:jar (attach-sources) > generate-sources @ adal4j >>>
[INFO] 
[INFO] <<< maven-source-plugin:2.2.1:jar (attach-sources) < generate-sources @ adal4j <<<
[INFO] 
[INFO] --- maven-source-plugin:2.2.1:jar (attach-sources) @ adal4j ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 27.151 s
[INFO] Finished at: 2016-05-16T16:39:49+02:00
[INFO] Final Memory: 25M/185M
[INFO] ------------------------------------------------------------------------

When using the jar resulted from the build to run public-client-adal4j-sample I'm getting this error:

com.microsoft.aad.adal4j.AuthenticationException: {"error_description":"AADSTS50034: To sign into this application the account must be added to the windows.net directory.\r\nTrace ID: 408f33c8-0716-476f-916c-7b5fbdca40c2\r\nCorrelation ID: ef2af04b-23a2-4f14-b001-d952374d11f8\r\nTimestamp: 2016-05-17 09:46:10Z","error":"invalid_grant"}
    at com.microsoft.aad.adal4j.AdalTokenRequest.executeOAuthRequestAndProcessResponse(AdalTokenRequest.java:108)
    at com.microsoft.aad.adal4j.AuthenticationContext.acquireTokenCommon(AuthenticationContext.java:819)
    at com.microsoft.aad.adal4j.AuthenticationContext.access$100(AuthenticationContext.java:66)
    at com.microsoft.aad.adal4j.AuthenticationContext$1.call(AuthenticationContext.java:174)
    at com.microsoft.aad.adal4j.AuthenticationContext$1.call(AuthenticationContext.java:163)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Answer:

I tried to reproduce the issue, but failed. I cloned the adal4j repository from GitHub, and packaged it via command mvn package successful.

Here is the steps within my environment.

OS: Ubuntu 14.04 LTS
Java SDK: JDK 1.8.0u51 64bit
Maven: 3.3.9
  1. Set up the envionments for JDK and Maven in the ~/.profile file.
export JAVA_HOME=~/<my-sdk-path>/jdk1.8.0_51
export CLASSPATH=.:$JAVA_HOME/lib
export PATH=$JAVA_HOME/bin:$PATH

export M2_HOME=~/<my-sdk-path>/apache-maven-3.3.9
export PATH=$PATH:$M2_HOME/bin
  1. Check the JDK & Maven
~ $ java -version
~ $ mvn -v
  1. Clone the current repository for the project adal4j.
~ $ git clone https://github.com/AzureAD/azure-activedirectory-library-for-java.git
  1. Package the adal4j project.
~ $ cd azure-activedirectory-library-for-java
~ $ mvn package

And I tried to package it via import the exsiting Maven project option and export the jar file in Eclipse, it also works successfully.

So I think the issue might be caused by the noise in the environment variables like PATH whatever you used Windows or Others. Please try to clean your environment or use some IDEs like Eclipse for compiling.

Hope it helps. Any concern, please feel free to let me know.

Question:

I have a similar issue to this post:Authenticate to Azure API App using ADAL but in my case I have a customer with a Java client hosted in JBoss who needs access to my API. The service is secured as 'Public (authenticated)' and I don't have any issues accessing it from a browser. I know that I can create an Azure API App Client in .net but I can't find any samples on how to authenticate from Java. Is this currently possible and if so does anyone have any samples or advice that would help?


Answer:

I reviewed some documents below to make a sample in Java for calling an Azure API app from client authenticated by AAD.

As references:

  1. https://azure.microsoft.com/en-us/documentation/articles/app-service-api-authentication-client-flow/
  2. https://azure.microsoft.com/en-us/documentation/articles/app-service-api-dotnet-add-authentication/
  3. https://azure.microsoft.com/en-us/documentation/articles/app-service-authentication-overview/

For the sample, I created a maven project in Eclipse and used libs adal4j, common-io & httpclient. Here is the dependencies configuration below in pom.xml file.

<dependencies>
    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>adal4j</artifactId>
        <version>1.1.2</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.1</version>
    </dependency>
</dependencies>

The sample code for service secured as Public (authenticated), please pay attention to comments in code.

    String gateway_url = "https://<GatewayHost>.azurewebsites.net/";
    String app_id_uri = gateway_url + "login/aad";
    String authority = "https://login.microsoftonline.com/<aad-domain>.onmicrosoft.com";
    String clientId = "<clientId>";
    String clientSecret = "<key>";
    String url = "https://<ApiAppHost>.azurewebsites.net/...";
/*
 *  Get Access Token from Gateway Login URL with authentication provider name
 *  Note: Please refer to the aad sample in Java for Native Headless at https://github.com/Azure-Samples/active-directory-java-native-headless
 */
HttpsURLConnection conn = (HttpsURLConnection) new URL(app_id_uri).openConnection();
AuthenticationContext context = null;
    AuthenticationResult result = null;
    ExecutorService service = null;
    try {
        service = Executors.newFixedThreadPool(1);
        context = new AuthenticationContext(authority, false, service);
        ClientCredential credential = new ClientCredential(clientId, clientSecret);
        Future<AuthenticationResult> future = context.acquireToken(app_id_uri, credential, null);
        result = future.get();
    } finally {
        service.shutdown();
    }
    String accessToken = null;
    if (result == null) {
        throw new ServiceUnavailableException(
                "authentication result was null");
    } else {
        accessToken = result.getAccessToken();
        System.out.println("Access Token: " +accessToken);
    }
    /*
     * Using access token to get authentication token
     */
    String data = "{\"access_token\": \""+accessToken+"\"}";
    conn.setRequestMethod("POST");
    conn.setDoOutput(true);
    conn.addRequestProperty("Content-Length", data.length()+"");
    new DataOutputStream(conn.getOutputStream()).writeBytes(data);
    String authTokenResp = IOUtils.toString(conn.getInputStream());
    System.out.println("Get Authentication Token Response: " + authTokenResp);
    /*
     * The content of Authentication Token Response is as {"user": {"userId": "sid:xxx...xxx"}, "authenticationToken": "xxxx...xxxxx"}.
     * Need to extract the authenticationToken from Json.
     */
    Gson gson = new Gson();
    Map<String, Object> map = gson.fromJson(authTokenResp, Map.class);
    String authenticationToken = (String) map.get("authenticationToken");
    System.out.println("Authentication Token: "+authenticationToken);
    /*
     * Using authentication token as X-ZUMO-AUTH header to get data from Api App
     * Note: Must using Apache Common HttpClient supported HTTP 30x redirection, Class Http(s)URLConnection not support.
     *          There are three times continuous 302 redirection in accessing Api App with zumo token. 
     */
    HttpGet httpGet = new HttpGet(url);
    httpGet.addHeader("x-zumo-auth", authenticationToken);
    CloseableHttpClient httpclient = HttpClients.createDefault();
    HttpResponse resp = httpclient.execute(httpGet);
    String apiAppData = IOUtils.toString(resp.getEntity().getContent());
    System.out.println(apiAppData);

Any concern, please feel free to let me know.

Question:

I am following the following two tutorials of the Azure documentation: https://docs.microsoft.com/en-us/azure/java/spring-framework/deploy-spring-boot-java-app-with-maven-plugin which shows how to deploy a simple Spring Boot application to Azure and https://docs.microsoft.com/en-us/azure/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-active-directory to set up and use an active directory as OAuth2 server with a Spring Security OAuth2 Client. Basically I just add the OAuth2 dependencies to Maven, a WebSecurityConfig class as shown in the second document and additionally also the azure.activedirectoy and spring.security properties.

When the application is just run from my local computer the login and redirection works fine. But when the application is deployed to Azure, I get an application error saying: Invalid Redirect URI Parameter. I think I have set the redirect-uri correctly as

https://{baseHost}{basePort}{basePath}/login/oauth2/code/azure

in the application properties as well as in the application registration with my Active Directory.

As far as I can see the authorization request uses the right parameters:

response_type: code
client_id: 4b5fbcfd-c35f-4bab-bc45-374c7e1dead8
scope: openid https://graph.microsoft.com/user.read
state: yMvo62R-6vgjETSGr_mnh4iIMZimVnFYZRyiGFaOPtE=
redirect_uri: https://myappname.azurewebsites.net/login/oauth2/code/azure
nonce: FUXJ5GoJ2NuNVx2ORU70YCqnJkEj8FRYHEJYMutEQzo

So, what could the Invalid Redirect URI Parameter be, and how can I change this?


Answer:

I followed these two tutorials, it works fine on local environment, on Azure webapp, I encountered redirect url mismatch error.

The cause is that the redirect_uri is always started with http. After adding server.forward-headers-strategy=native in applications.properties, it works. (I am using spring boot 2.2)

Here is the pom.xml for your reference.

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <parent> 
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-parent</artifactId>  
    <version>2.2.4.RELEASE</version>  
    <relativePath/>  
    <!-- lookup parent from repository --> 
  </parent>  
  <groupId>com.example.ad</groupId>  
  <artifactId>ad</artifactId>  
  <version>0.0.1-SNAPSHOT</version>  
  <name>ad</name>  
  <description>Demo project for Spring Boot</description>  
  <properties> 
    <java.version>1.8</java.version>  
    <azure.version>2.2.0</azure.version> 
  </properties>  
  <dependencies> 
    <dependency> 
      <groupId>org.springframework.boot</groupId>  
      <artifactId>spring-boot-starter-security</artifactId> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework.boot</groupId>  
      <artifactId>spring-boot-starter-web</artifactId> 
    </dependency>  
    <dependency> 
      <groupId>com.microsoft.azure</groupId>  
      <artifactId>azure-active-directory-spring-boot-starter</artifactId> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework.boot</groupId>  
      <artifactId>spring-boot-starter-test</artifactId>  
      <scope>test</scope>  
      <exclusions> 
        <exclusion> 
          <groupId>org.junit.vintage</groupId>  
          <artifactId>junit-vintage-engine</artifactId> 
        </exclusion> 
      </exclusions> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework.security</groupId>  
      <artifactId>spring-security-test</artifactId>  
      <scope>test</scope> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework.security</groupId>  
      <artifactId>spring-security-oauth2-client</artifactId> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework.security</groupId>  
      <artifactId>spring-security-oauth2-jose</artifactId> 
    </dependency> 
  </dependencies>  
  <dependencyManagement> 
    <dependencies> 
      <dependency> 
        <groupId>com.microsoft.azure</groupId>  
        <artifactId>azure-spring-boot-bom</artifactId>  
        <version>${azure.version}</version>  
        <type>pom</type>  
        <scope>import</scope> 
      </dependency> 
    </dependencies> 
  </dependencyManagement>  
  <build> 
    <plugins> 
      <plugin> 
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-maven-plugin</artifactId> 
      </plugin>  
      <plugin> 
        <groupId>com.microsoft.azure</groupId>  
        <artifactId>azure-webapp-maven-plugin</artifactId>  
        <version>1.9.0</version>  
        <configuration>
          <schemaVersion>V2</schemaVersion>
          <resourceGroup>ad-1582615028467-rg</resourceGroup>
          <appName>ad-1582615028467</appName>
          <pricingTier>P1v2</pricingTier>
          <region>westeurope</region>
          <runtime>
            <os>linux</os>
            <javaVersion>jre8</javaVersion>
            <webContainer>jre8</webContainer>
          </runtime>
            <appSettings>
                <property>
                    <name>JAVA_OPTS</name>
                    <value>-Dserver.port=80</value>
                </property>
            </appSettings>
          <deployment>
            <resources>
              <resource>
                <directory>${project.basedir}/target</directory>
                <includes>
                  <include>*.jar</include>
                </includes>
              </resource>
            </resources>
          </deployment>
        </configuration>
      </plugin> 
    </plugins> 
  </build> 
</project>

Question:

Title I guess is self explanatory.

I haven't tried anything yet as I haven't seen a sample approach from Microsoft.

The end result should be that when I go to Azure portal and go to Active directory, the user deleted will be back in the Active Users list (Removed from "Deleted Users").

Thanks!


Answer:

You can use Microsoft Graph SDK for Java to do this.

graphClient.directory().deletedItems("{deletedItemId}").restore();

Or you can call graph api directly.

POST /directory/deletedItems/{id}/restore

Question:

I am trying to authenticate and app and get bearer token for further use. I get the error which is title of this thread.

Another thread describes same thing except that my code is in Java. the workaround is to use certificate method. "To sign into this application the account must be added to the domain.com directory"

Can someone please describe detailed steps for this workaround :- certificate method Or how can i fix the below code with any other method Or is any other method to achieve this whole task

Here is my code

private final static String AUTHORITY = "https://login.microsoftonline.com/<tenantId>/OAuth2/Authorize";
private final static String CLIENT_ID = "<Client_Id>";
private final static String CLIENT_SECRET = "<Secret>";

public static void main(String args[]) throws Exception {

    try (BufferedReader br = new BufferedReader(new InputStreamReader(
            System.in))) {

        String username = CLIENT_ID;
        String password = CLIENT_SECRET;

        service = Executors.newFixedThreadPool(1);

        context = new AuthenticationContext(AUTHORITY, false, service);
        Future<AuthenticationResult> future = context.acquireToken(
                "https://graph.microsoft.com", CLIENT_ID, username, password,
                null);
        result = future.get();

    }
    finally {
        service.shutdown();
    }

}

I have registered my app in AAD app registration. The Client_Id is App Id and Secret is a key in the above code


Answer:

In case, you want to make the code work in it's current form without any workaround, check for following things -

  1. Make sure you have the correct tenantId GUID specified in the first line of code.

    Steps to get tenantid -

    Login to Azure Portal, Navigate to your Azure AD, Go to properties like in screenshot below and Directory ID should give you the GUID.

 private final static String AUTHORITY = "https://login.microsoftonline.com/<tenantId>/OAuth2/Authorize";
  1. Make sure that Username you are using, is for a user that belongs to your AzureAD tenant.

    One possible reason could be if you're using a Microsoft account like xyz@outlook.com or hotmail.com etc. Try using an account that is created in this Azure AD like xyz@yourtenantdomain.onmicrosoft.com or any other verified domain that your tenant uses.

Future<AuthenticationResult> future = context.acquireToken("https://graph.microsoft.com", CLIENT_ID, username, password, null);

Question:

I am trying to implement Azure AD cloud user account policy in Java using regex.

I have defined a pattern string and I use string.matches(pattern) as follows:

// ONE OF THESE CHARACTER CLASS COMBINATIONS NEED TO BE MET
// combination of lowercase, uppercase and numbers, 8 to 16 characters in length
// combination of lowercase, uppercase and symbols, 8 to 16 characters in length
// combination of lowercase, numbers and symbols, 8 to 16 characters in length
// combination of uppercase, numbers and symbols 8 to 16 characters in length
private static final String PATTERN_PASSWORD_REGEX = new StringBuilder('^')
        .append("((?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])).{8,16}")
        .append('|') // -or-
        .append("((?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&*\\-_!+=\\[\\]{}|\\:‘,.?\\/`~"\\(\\);])).{8,16}")
        .append('|') // -or-
        .append("((?=.*[a-z])(?=.*[0-9])(?=.*[@#$%^&*\\-_!+=\\[\\]{}|\\:‘,.?\\/`~"\\(\\);])).{8,16}")
        .append('|') // -or-
        .append("((?=.*[A-Z])(?=.*[0-9])(?=.*[@#$%^&*\\-_!+=\\[\\]{}|\\:‘,.?\\/`~"\\(\\);])).{8,16}")
        .append('$')
        .toString();

public static boolean validate(String password) {
    return password.matches(PATTERN_PASSWORD_REGEX);
}

Somehow, it is treating spaces as a special character, even though I have not included it in the regex. The JUnit test method below is passing on all the assertions except the last one.

@Test
public void testPassword() {

    Assert.assertTrue("password is valid", validate("Abcdef12"));
    Assert.assertTrue("password is valid", validate("abcde$12"));
    Assert.assertTrue("password is valid", validate("ABCDE%12"));
    Assert.assertTrue("password is valid", validate("Abcde~12"));
    Assert.assertFalse("password is invalid", validate("abcdefgh"));
    Assert.assertFalse("password is invalid", validate("12345678"));
    Assert.assertFalse("password is invalid", validate("ABCDEFGH"));
    Assert.assertFalse("password is invalid", validate("ABCDefgh"));
    Assert.assertFalse("password is invalid", validate("!~$:-_@&"));

    // This assertion fails...
    Assert.assertFalse("password is invalid", validate("Abcdef 12"));
}

I have seen very elegant answers for regex questions on SO, but generally they don't involve character classes combinations. Hope someone can tell me what I am doing wrong, or if there is a much better way of doing this.


Answer:

You need to specify those chars you allow in the last part, instead of the dot. Replace .{8,16} with [-A-Za-z0-9@#$%^&*_!+=\[\]{}|:',.?/`~\"();]{8,16}:

^(?:
 (?=.*[a-z])(?=.*[A-Z])(?=.*\d)
 |
 (?=.*[a-z])(?=.*[A-Z])(?=.*[-@#$%^&*_!+=\[\]{}|:',.?/`~"();])
 |
 (?=.*[a-z])(?=.*\d)(?=.*[-@#$%^&*_!+=\[\]{}|:',.?/`~"();])
 |
 (?=.*[A-Z])(?=.*\d)(?=.*[-@#$%^&*_!+=\[\]{}|:',.?/`~"();])
)
[-A-Za-z0-9@#$%^&*_!+=\[\]{}|:',.?/`~"();]{8,16}
$

See the regex demo.

private static final String PATTERN_PASSWORD_REGEX = new StringBuilder('^(?:')
    .append("(?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z])(?=[^0-9]*[0-9])")
    .append('|') // -or-
    .append("(?=[^a-z]*[a-z])(?=[^A-Z]*[A-Z])(?=.*[-@#$%^&*_!+=\\[\\]{}|:‘,.?/`~"();])")
    .append('|') // -or-
    .append("(?=[^a-z]*[a-z])(?=[^0-9]*[0-9])(?=.*[-@#$%^&*_!+=\\[\\]{}|:‘,.?/`~"();])")
    .append('|') // -or-
    .append("(?=[^A-Z]*[A-Z])(?=[^0-9]*[0-9])(?=.*[-@#$%^&*_!+=\\[\\]{}|:‘,.?/`~"();])")
    .append(')[-A-Za-z0-9@#$%^&*_!+=\\[\\]{}|:‘,.?/`~"();]{8,16}$')
    .toString();

Question:

We integrated our Java app with Azure Active Directory. So the issue that we’re having at the moment is that Azure Active Directory Session is not destroyed when the Spring Security session is destroyed (for example if Tomcat is restarted on the WEB server)

Is there way to tell which parameter identifies Azure Active Directory session? Or in general would you have any suggestions on how to handle this in the best way?

We used this example to integrate our Java app with Active Directory.

https://azure.microsoft.com/en-us/resources/samples/active-directory-java-webapp-openidconnect/

Thanks,

Andy G.


Answer:

There is the section Send a sign-out request of the offical document Authorize access to web applications using OpenID Connect and Azure Active Directory introduces how to sign the user out of your app, as below.

Send a sign-out request

When you wish to sign the user out of the app, it is not sufficient to clear your app's cookies or otherwise end the session with the user. You must also redirect the user to the end_session_endpoint for sign-out. If you fail to do so, the user will be able to reauthenticate to your app without entering their credentials again, because they will have a valid single sign-on session with the Azure AD endpoint.

You can simply redirect the user to the end_session_endpoint listed in the OpenID Connect metadata document:

GET https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F

Parameter: post_logout_redirect_uri recommended

Description: The URL that the user should be redirected to after successful logout. If not included, the user is shown a generic message.

Also, there is an offical sample in C# Azure-Samples/active-directory-dotnet-web-single-sign-out which you can refer to.

Question:

I'm using Java to create a simple daemon or service app (not Web app) to call an Office 365 Calendar API. I've followed this guide Call Microsoft Graph in a service or daemon app, but when I try to call the API with the access token, I obtain a 401 error. I've registered the app to azure portal with all the Graph authorizations and I've made the certificate, following the Step 1 of this guide: Get a certificate or create a self-signed certificate. Here is my code for the access token request:`

    String accessToken="";
    String token_endpoint = "https://login.windows.net/<mytenant>/oauth2/token";
    String grant_type = "client_credentials";
    String client_secret = <mysecret>;
    String resource = "https://graph.microsoft.com";
    String client_id = <myclient>;

    CloseableHttpClient httpclient = HttpClients.createDefault();
    HttpPost httpPost = new HttpPost(token_endpoint);
    List<NameValuePair> parameters = new ArrayList<>();
    parameters.add(new BasicNameValuePair("grant_type", grant_type));
    parameters.add(new BasicNameValuePair("client_id", client_id));
    parameters.add(new BasicNameValuePair("client_secret", client_secret));
    parameters.add(new BasicNameValuePair("resource", resource));
    httpPost.setEntity(new UrlEncodedFormEntity(parameters));
    httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");

    try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
        System.out.println(response.getStatusLine());
        HttpEntity entity = response.getEntity();

        //parsing
        JSONParser parser = new JSONParser();
        Scanner httpResponseScanner = new Scanner(entity.getContent());
        String jsonString = httpResponseScanner.nextLine();
        //System.out.println(jsonString);
        JSONObject json = (JSONObject) parser.parse(jsonString);
        accessToken = json.get("access_token").toString();

        EntityUtils.consume(entity);
    }
    return accessToken;`

And here is my API call code:`

     String apiURL = "https://outlook.office.com/api/v2.0/me/calendars";
     CloseableHttpClient httpclient = HttpClients.createDefault();
     HttpGet httpGet = new HttpGet(apiURL);
     httpGet.addHeader("Accept", "application/json");
     httpGet.addHeader("Authorization", "Bearer " + accessToken);

     try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
         System.out.println(response.getStatusLine());
         HttpEntity entity = response.getEntity();
         EntityUtils.consume(entity);
     }`

I've already tested my access token to jwt.io with invalid signature response, so I think there is something wrong in my token request. Someone can help me?


Answer:

From your code , you are acquiring token for resource :https://graph.microsoft.com , but in api calls with that token , you are calling the outlook mail rest api (https://outlook.office.com/) . If you want to call microsoft graph api (https://graph.microsoft.com ) , you should check the microsoft graph api get calendars .

The second problem is you are using client credential flow (app's identity) , you can't use user identity (/me) because there is no user information includes in access token . With microsoft graph api , you could use GET /users/{id | userPrincipalName}/calendars

Question:

I am new to Azure related concepts and am facing issue in connecting the Azure Key vault.

Please find my code snippets as follows and let me know why am getting the below exception:

Get Key started.../n SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. Get Key failedjava.lang.RuntimeException: java.util.concurrent.ExecutionException: com.microsoft.aad.adal4j.AuthenticationException: {"error_description":"AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.\r\nTrace ID: 13f8e909-89d8-472f-a1c1-9f4bcf693700\r\nCorrelation ID: bf818c41-4092-4f7d-8292-b1275a5da62f\r\nTimestamp: 2017-10-17 07:22:12Z","error":"invalid_client"} Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.util.concurrent.ExecutionException: com.microsoft.aad.adal4j.AuthenticationException: {"error_description":"AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.\r\nTrace ID: 1234\r\nCorrelation ID: 123456\r\nTimestamp: 2017-10-17 07:22:12Z","error":"invalid_client"} at com.google.common.util.concurrent.AbstractFuture$Sync.getValue(AbstractFuture.java:299) at com.google.common.util.concurrent.AbstractFuture$Sync.get(AbstractFuture.java:286) at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:116) at Program.main(Program.java:88)

Corresponding Code am trying to connect Azure Key Vault :

KeyVaultCredentials kvCred = new ClientSecretKeyVaultCredential("clientID", "client Secret");
    KeyVaultClient vc = new KeyVaultClient(kvCred);



    byte[] byteText = textToEncrypt.getBytes("UTF-16");

    /*************************************/

    // Get Key from Key Vault
    System.out.println("Get Key started.../n");

    start = System.currentTimeMillis();
    ServiceCallback<KeyBundle> serviceCallbackgetkey = new ServiceCallback<KeyBundle>(){
        @Override
        public void failure(Throwable t) {
            System.out.println("Get Key failed"+t.toString());

        }

        @Override
        public void success(KeyBundle result ) {//ServiceResponse
            System.out.println("Get Key Success");
            JsonWebKey myKey = result.key();
            keyIdentifier =  myKey.kid();
            System.out.println("Key ID:"+keyIdentifier);
            end = System.currentTimeMillis();       
            formatter = new DecimalFormat("#0.00000");
            System.out.print("Get Key Execution time is " + formatter.format((end - start) / 1000d) + " seconds\n");
            start = 0;
            end =0;
         }
    };

    ServiceCall<KeyBundle> call = vc.getKeyAsync(keyVaultURI, "MyKey1", serviceCallbackgetkey);

    System.out.println(call.get());

Note: Am using the same Client-ID and Client Secret in postman for connecting a different REST api and is working fine.

Also, I tried executing the following code from here. But facing same issue.

Please help me identifying why am unable to connect the vault.


Answer:

I tried to reproduce your issue but failed.

I thought your issue probably results from the permission to authorize keyvault API for your application.

You could refer to the code below which works for me.

Program Class:

import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutionException;

import com.microsoft.azure.keyvault.KeyVaultClient;
import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials;


public class Program {

    public static void main(String[] args)
            throws InterruptedException, ExecutionException, URISyntaxException, UnsupportedEncodingException {

        KeyVaultCredentials kvCred = new ClientSecretKeyVaultCredential("APP_ID", "APP_SECRET");
        KeyVaultClient vc = new KeyVaultClient(kvCred);
        String keyIdentifier = "https://jaygong.vault.azure.net/keys/jaytest/b21bae081025418c806d73affc2937e0";
        System.out.println(vc.getKey(keyIdentifier));

    }
}

ClientSecretKeyVaultCredential Class:

import java.net.MalformedURLException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials;

public class ClientSecretKeyVaultCredential extends KeyVaultCredentials {
    private String applicationId;
    private String applicationSecret;

    public ClientSecretKeyVaultCredential(String applicationId, String applicationSecret) {
        this.setApplicationId(applicationId);
        this.setApplicationSecret(applicationSecret);
    }

    public String getApplicationId() {
        return applicationId;
    }

    private void setApplicationId(String applicationId) {
        this.applicationId = applicationId;
    }

    public String getApplicationSecret() {
        return applicationSecret;
    }

    private void setApplicationSecret(String applicationSecret) {
        this.applicationSecret = applicationSecret;
    }

    @Override
    public String doAuthenticate(String authorization, String resource, String scope) {
        AuthenticationResult res = null;

        try {
            res = GetAccessToken(authorization, resource, applicationId, applicationSecret);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return res.getAccessToken();
    }

    private AuthenticationResult GetAccessToken(String authorization, String resource, String clientID, String clientKey)
            throws InterruptedException, ExecutionException {
        AuthenticationContext ctx = null;
        ExecutorService service = Executors.newFixedThreadPool(1);
        try {
            ctx = new AuthenticationContext(authorization, false, service);
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Future<AuthenticationResult> resp = ctx.acquireToken(resource, new ClientCredential(
                clientID, clientKey), null);
        AuthenticationResult res = resp.get();
        return res;
    }

}

Results:

Please notice that you should authorize your application to use the key or secret. Here is the powershell way which is mentioned in the official doc.

Set-AzureRmKeyVaultAccessPolicy -VaultName 'XXXXXXX' -ServicePrincipalName XXXXX -PermissionsToKeys decrypt,sign,get,unwrapKey

Update Answer:

I'm not sure if your application has permission to call KeyVault API. You could add this permission on portal.

Hope it helps you.

Question:

// get metric definitions for storage account.
  for (MetricDefinition  metricDefinition : azure.metricDefinitions().listByResource(storageAccount.id())) {

Azure github has this example to get metrics for a storage account. I am struggling to find any reference on what should be passed as parameter to listByResource() to get VM Metric (for example Network In metric)? Appreciate any input.


Answer:

Firstly, if you want to get metrics supported by Azure Monitor, you could use this to query records. You could also find it in the sample code.

MetricCollection metricCollection = metricDefinition.defineQuery()
                        .startingFrom(recordDateTime.minusDays(7))
                        .endsBefore(recordDateTime)
                        .withAggregation("Average")
                        .withInterval(Period.minutes(5))
                        .withOdataFilter("apiName eq 'PutBlob' and responseType eq 'Success' and geoType eq 'Primary'")
                        .execute();

And about the method description, you could refer to this site.

As for the VM NetWork metric, I suppose it's not supported, in the official doc :Supported metrics with Azure Monitor on Azure Stack, it lists metrics supported by Azure Monitor. With Microsoft.Compute/virtualMachines, it only supports the Percentage CPU metric.

Question:

I have a Java Spring Boot web application that I am working on deploying and using Azure AD (Office365) as the user repository for Oauth. The application will not be deployed in Azure. I have the application working locally with the 'Home Page URL' set to http://localhost:8080. It authenticates properly and I have no problems there. My question is that how do I keep my localhost setting, in the app registration and add test or prod URL's along with that localhost URL for local development in the Azure App Registration?

I have used Google's Oauth stuff before and it has the ability to use multiple URL's for each app, solving this issue. I realize that it is possible to make an app registration per environment, but that seems excessive. Is there something I am not seeing or am I viewing this config incorrectly? Looking at the docs, it only mentions one URL for this.


Answer:

You add the callback URLs for each environment in the Reply URLs section. That allows authentication tokens to be returned to those URLs.

Then in your app, configure the redirect_uri parameter to be the current environment's callback URL.

The home page URL AFAIK is shown on some pages like the My Apps portal (myapps.microsoft.com), and it makes sense that if they want to link to your app from somewhere, only one URL is allowed for that. If you use one registration, then this should be your production URL. You can also use separate registrations for the different environments.

Question:

I am trying to send POST request to Microsoft Azure Graph API for User creation. I have referred their sample example and able to execute GET request successfully but not POST.

My code is as below:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.naming.ServiceUnavailableException;

import org.json.JSONException;
import org.json.JSONObject;

import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;

public class CreateUser {

    private final static String AUTHORITY = "https://login.microsoftonline.com/common/";
    private final static String CLIENT_ID = "<Client_id>";

    public static void main(String args[]) throws Exception {

        try (BufferedReader br = new BufferedReader(new InputStreamReader(
                System.in))) {
            System.out.print("Enter username: ");
            String username = br.readLine();
            System.out.print("Enter password: ");
            String password = br.readLine();

            // Request access token from AAD
            AuthenticationResult result = getAccessTokenFromUserCredentials(
                    username, password);
            // Get user info from Microsoft Graph
            String userInfo = createUserInGraph(result.getAccessToken());
            System.out.print(userInfo);
        }
   }

    private static AuthenticationResult getAccessTokenFromUserCredentials(
            String username, String password) throws Exception {
        AuthenticationContext context;
        AuthenticationResult result;
        ExecutorService service = null;
        try {
            service = Executors.newFixedThreadPool(1);
            context = new AuthenticationContext(AUTHORITY, false, service);
            Future<AuthenticationResult> future = context.acquireToken(
                    "https://graph.microsoft.com", CLIENT_ID, username, password,
                    null);
            result = future.get();
        } finally {
            service.shutdown();
        }

        if (result == null) {
            throw new ServiceUnavailableException(
                    "authentication result was null");
        }
        return result;
    }

    private static String createUserInGraph(String accessToken) throws IOException {

        String jsonInputData = "{  \"accountEnabled\": true," + 
                "  \"city\": \"Delhi\"," + 
                "  \"country\": \"India\"," + 
                "  \"department\": \"Human Resources\"," + 
                "  \"displayName\": \"Adam G\"," + 
                "  \"givenName\": \"Adam\"," + 
                "  \"jobTitle\": \"Senior Human Resource Manager\"," + 
                "  \"mailNickname\": \"adamg\"," + 
                "  \"passwordPolicies\": \"DisablePasswordExpiration\"," + 
                "  \"passwordProfile\": {" + 
                "    \"password\": \"Test1234\"," + 
                "    \"forceChangePasswordNextSignIn\": false" + 
                "  }," + 
                "  \"officeLocation\": \"131/1105\"," + 
                "  \"postalCode\": \"98052\"," + 
                "  \"preferredLanguage\": \"en-US\"," + 
                "  \"state\": \"MH\"," + 
                "  \"streetAddress\": \"9256 Towne Center Dr., Suite 400\"," + 
                "  \"surname\": \"Gily\"," + 
                "  \"mobilePhone\": \"+91 02030713231\"," + 
                "  \"usageLocation\": \"IND\"," + 
                "  \"userPrincipalName\": \"adamg@alandonaldgmail.onmicrosoft.com\"}";

        System.out.println("Input: " + jsonInputData);
        URL url = new URL("https://graph.microsoft.com/v1.0/users");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Authorization", "Bearer " + accessToken);
        System.out.println("Access Token: \n" + accessToken);
        System.out.println();
        conn.setRequestProperty("Content-type","application/json; charset=UTF-8");

        OutputStream os = conn.getOutputStream();
        os.write(jsonInputData.getBytes("UTF-8"));
        os.close();
        //display what returns the POST request

        StringBuilder sb = new StringBuilder();  
        int HttpResult = conn.getResponseCode();
        System.out.println("Response code: " + HttpResult);
        if (HttpResult == HttpURLConnection.HTTP_OK) {
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line = null;  
            while ((line = br.readLine()) != null) {  
                sb.append(line + "\n");  
            }
            br.close();
            System.out.println("" + sb.toString());  
        } else {
            System.out.println(conn.getResponseMessage());  
        }
        return null;
    }
}

So here I am getting error code as 400 and error message as 'Bad request'. Can somebody explain me what is the issue?


Answer:

Bad Request issues are usually related to some invalid data/formatting being sent.

Looking at your json, two things stand out. Please make these changes and see if this resolves your issue.

  1. "userPrincipalName": "adamg@alandonaldgmail.onmicrosoft.com" (make sure alandonaldgmail.onmicrosoft.com is a verified domain for your azure active directory organization, if it is not and you used it by mistake, then change the value for userPrincipalName to something like "adamg@yourazureadtenantname.onmicrosoft.com")

  2. "usageLocation": "IND" (this should probably be just "IN", as it's supposed to be a two letter country code (ISO Standard 3166))

Referencing these from here - Update User API Reference

Question:

I have authenticated successfully against azure AD (https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize) from of my angular application and got the id_token. The id_token is passed to another multiple trusted spring boot REST application

From the trusted spring boot REST application which can I validate the id_token if it is valid or not. Do we have any endpoint or out of box classes to check the validity of the id_token .ie to check if the id_token is from the same application and is also valid. The trusted spring boot application have all the below details

azure.activedirectory.tenant-id=xxxxx...
azure.activedirectory.client-id=xxxxxx...
azure.activedirectory.client-secret=xxxxx...
azure.activedirectory.active-directory-groups=Users

Can anyone please help me on this

Do let me know if need more details on anywhere


Answer:

You can directly check some claims' value of the id_token to check if the id_token is from the same application and is also valid by decoding id_token.

For example, you can check the aud value to find this id_token is for which app, the aud vaule is your app's Application ID; you can use iat, exp, andnbf three claims' value to check the token is valid or not. Hereiat value is the time that token issued, nbf value is the time that token becomes valid, and exp value is the time that the token becomes invalid.

For the details, please read v2.0 tokens.

Question:

I have a simple web app with a Java API backend using JSP and Tomcat. How can I get the current logged in user (logged in via Azure AD)?

I'm trying this:

<%
// Fetch the data
URL url = new URL("https://myurl.azurewebsites.net/.auth/me");

Cookie cookie = null;
Cookie[] cookies = request.getCookies();
URLConnection connection = url.openConnection();

for(int i=0; i < cookies.length; i++) {
   cookie = cookies[i];
   connection.addRequestProperty(cookie.getName(), cookie.getValue());
}

// Convert to a JSON object
JsonParser jp = new JsonParser();
JsonElement root = jp.parse(new InputStreamReader(connection.getInputStream()));
JsonObject rootobj = root.getAsJsonObject();

String userID = rootobj.get("user_id").getAsString();

But this is getting a 401 Unauthorized error. However I can get the JSON from /.auth/me in the browser - presumably because it's sending the session cookie with it. Any ideas how I can fetch that JSON from my JSP?


Answer:

According to the code you offered, I guess you want to receive the incoming cookie in the JSP and simulate the request URL https://myurl.azurewebsites.net/.auth/me to get JSON data.

However,you missed the Header part of the request, which is a necessary condition for Authentication of Azure Active Directory.

You could refer to the snippet of code as below and retry your request.

<%
// Fetch the data
URL url = new URL("https://myurl.azurewebsites.net/.auth/me");

Cookie cookie = null;
Cookie[] cookies = request.getCookies();

HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
for(int i=0; i < cookies.length; i++) {
   cookie = cookies[i];
   connection.addRequestProperty(cookie.getName(), cookie.getValue());

}
Enumeration<String> headNames = request.getHeaderNames();
while(headNames.hasMoreElements()) {
    String headerName = headNames.nextElement();
    String headerVal = request.getHeader(headerName);
    connection.addRequestProperty(headerName, headerVal);
}

out.println("generate connection.....");

connection.connect();

IOUtils.copy(new InputStreamReader(connection.getInputStream()),out);

%>

Hope it helps you.

Question:

I wanted to play around with Microsoft Azure's Active Directory Library for Java. After I pulled the code from github and importing to Eclipse as a maven project, building and executing the PublicClient.java sample file, I get the following exception:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" java.util.concurrent.ExecutionException: java.net.MalformedURLException: no protocol: 
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at src.main.java.PublicClient.getAccessTokenFromUserCredentials(PublicClient.java:36)
    at src.main.java.PublicClient.main(PublicClient.java:23)
Caused by: java.net.MalformedURLException: no protocol: 
    at java.net.URL.<init>(URL.java:585)
    at java.net.URL.<init>(URL.java:482)
    at java.net.URL.<init>(URL.java:431)
    at com.microsoft.aad.adal4j.HttpHelper.openConnection(HttpHelper.java:110)
    at com.microsoft.aad.adal4j.HttpHelper.executeHttpGet(HttpHelper.java:43)
    at com.microsoft.aad.adal4j.HttpHelper.executeHttpGet(HttpHelper.java:38)
    at com.microsoft.aad.adal4j.MexParser.getWsTrustEndpointFromMexEndpoint(MexParser.java:87)
    at com.microsoft.aad.adal4j.AuthenticationContext.processPasswordGrant(AuthenticationContext.java:852)
    at com.microsoft.aad.adal4j.AuthenticationContext.access$0(AuthenticationContext.java:839)
    at com.microsoft.aad.adal4j.AuthenticationContext$1.call(AuthenticationContext.java:129)
    at com.microsoft.aad.adal4j.AuthenticationContext$1.call(AuthenticationContext.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

I have correctly specified my client ID, username and password. Can someone please help me resolve this issue?


Answer:

I reproduce your issue. The problem is that the sample PublicClient.java is a separate Maven project with Microsoft AAD Library for Java.

To resolve this issue, you need to import the sample project alone to Eclipse as the follows.

Then you run the maven install to install all depencies for the sample project and execute it. It will works without exception.

Question:

I'm using the MS Azure Graph API to get a list of all groups in an instance of Azure AD (actually the Azure AD instance sitting underneath an Office 365 account) but my results don't include the Dynamic Distribution Group I've created (in O365).

Should I see dynamic distribution groups in the results of a "give me all groups" query like this one?

https://graph.windows.net/contoso.com/groups?api-version=2013-04-05

I'm assuming I should be able to, and that either there's a problem with the group itself or I'm just missing something.

For reference, my O365 instance contains

  • 3 users
  • 1 security group
  • 1 "normal" distribution group
  • 1 dynamic distribution group

The group query returns only the first two groups and not the dynamic one.

Also possibly relevant: it's a pure Office 365 implementation. I.e. not a hybrid On-Premise instance being synced with an Office365 instance.


Answer:

Unfortunately no. Dynamic distribution lists live in Exchange/Online not in the Directory - they are not accessible via the Directory Graph API. You need to use the Exchange Shell to manage them programmatically.

Question:

I'm trying several ways to use the Microsoft Graph api(beta version) with JAVA. (I refer this link to connect api "https://docs.microsoft.com/en-us/graph/tutorials/java") And I could use all the functions of v1.0, but I couldn't of beta version.

For example, When I request below code, they successfully return the JsonObject.

JsonObject response = graphClient
        .customRequest("/me/calendar")
        .buildRequest()
        .get();

But when I request below code,

JsonObject response = graphClient
        .customRequest("/me/findRoomLists")
        .buildRequest()
        .get();

they return below error

There was an unexpected error (type=Internal Server Error, status=500).
Error code: ErrorAccessDenied Error message: Access is denied. Check credentials and try again. GET https://graph.microsoft.com/beta//me/findRoomLists SdkVersion : graph-java/v1.5.0 Authorization : Bearer

I don't know which is the problem...

I use the code below to use the beta version.

    // Build a Graph client
    graphClient = GraphServiceClient.builder()
        .authenticationProvider(authProvider)
        .logger(logger)
        .buildClient();

    //version change 1.0 -> beta
    graphClient.setServiceRoot("https://graph.microsoft.com/beta/");

And This is my API permissions in Azure.

1. Calendars.ReadWrite
2. Mail.ReadWrite
3. User.ReadBasic.All
4. User.ReadWrite

App scope

app.scopes=User.ReadWrite,Calendars.ReadWrite,Mail.ReadWrite

What should I modify to use the beta version? I'm sorry if it's a duplicate question.


Answer:

The Microsoft Graph Java SDK just contains the models and request builders for accessing the v1.0 endpoint with the fluent API. (There will be a java SDK sample for a specific API if it is existing, otherwise not)

Even if you use graphClient.setServiceRoot("https://graph.microsoft.com/beta/"), it will still serialize/deserialize everything using the /v1.0 classes.

If you want to use resources/properties that don't exist in v1.0, then you'll need to generate the SDK code yourself using the beta manifests.

For more details, check this link, the guy @Marc LaFleur works in the Microsoft Graph product team mentioned it.

Question:

I am implementing jose4j in my Java application to verify the signature of the access token issued by Azure. The application works fine, however, I came across this documentation about Signing Key rollover. Does jose4j take care of it automatically when using the HttpsJwksVerificationKeyResolver?

I am currently using the following snippet to build the JwtConsumer

String azureKeyDiscoveryUrl =
                "https://login.microsoftonline.com/{my-tenant-id}/discovery/keys";
HttpsJwks azureKeyDiscovery = new HttpsJwks(azureKeyDiscoveryUrl);

HttpsJwksVerificationKeyResolver azureJwksKeyResolver = new HttpsJwksVerificationKeyResolver(azureKeyDiscovery);

JwtConsumer azureJwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime()
                .setAllowedClockSkewInSeconds(30)
                .setRequireIssuedAt()
                .setRequireNotBefore()
                .setVerificationKeyResolver(azureJwksKeyResolver)
                .setExpectedAudience("my-audience")
                .setJwsAlgorithmConstraints(new AlgorithmConstraints(
                        AlgorithmConstraints.ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_USING_SHA256))
                .build();
JwtClaims claims = azureJwtConsumer.processToClaims("tokenStringHere");

Answer:

Yes, assuming Azure does the right/reasonable thing with the https://login.microsoftonline.com/{my-tenant-id}/discovery/keys endpoint, which I think they do, it'll work.

Question:

Hi We have created a native application in AAD and we are trying to get the access token from Azure using Adal4j in our java application following is the snippet

AuthenticationContext authContext;
    AuthenticationResult authResult;
    String loginUrl="https://login.microsoftonline.com/"+TENANTID+"/oauth2/authorize";
    service = Executors.newFixedThreadPool(1);
    authContext = new AuthenticationContext(loginUrl, false, service);
    Future<AuthenticationResult> future = authContext.acquireToken("https://management.azure.com/", clientId, username,password, null);
    authResult = future.get();
    System.out.println("Token :"+authResult.getAccessToken());

but recently our organisation has enabled multi factor authentication and since then we are getting the below error.

java.util.concurrent.ExecutionException: com.microsoft.aad.adal4j.AuthenticationException: {"error_description":"AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access '797f4846-ba00-4fd7-ba43-dac1f8f63013'.\r\nTrace ID: c0ac222e-5a9b-417e-8360-d42712f43c00\r\nCorrelation ID: 96cc11c4-3196-448d-a416-a9373b8059a6\r\nTimestamp: 2018-08-13 12:57:59Z","error":"interaction_required"}

Please help me on how i can fix this error ADAL4j version is 1.1.3

I have created app registrations using below

Please let me know what im missing as i see it is displaying for your account bt not for my account. MFA is enabled for my tenant.


Answer:

Please help me on how i can fix this error ADAL4j version is 1.1.3

If the user is enabled with multi factor authentication, we can't use that to get access token to access the resource directly.

The security of two-step verification lies in its layered approach. Compromising multiple authentication factors presents a significant challenge for attackers. Even if an attacker manages to learn the user's password, it is useless without also having possession of the additional authentication method. It works by requiring two or more of the following authentication methods:

  • Something you know (typically a password)
  • Something you have (a trusted device that is not easily duplicated, like a phone)
  • Something you are (biometrics)

we are trying to get the access token from Azure using Adal4j in our java application

You could registry an Azure AD web application and use the following way to instead of using the username and password to get the access token. How to registry Azure AD webapp and get the secret key from it, please refer to this tutorial.

String resourceUrl = "https://management.azure.com";
AuthenticationContext context = new AuthenticationContext(authority, true, service);
// Acquire Token
Future<AuthenticationResult> result = context.acquireToken(
resourceUrl,
new ClientCredential(APP_ID, APP_SECRET),
null
);
String token = result.get().getAccessToken();

Update:

Add the demo code for geting subscription. If you get the empty list means that there is no subscription with your tenant.

String authority = "https://login.microsoftonline.com/{tenantId}";
String resourceUrl = "https://management.azure.com";
ExecutorService service = Executors.newFixedThreadPool(1);
AuthenticationContext context = new AuthenticationContext(authority, true, service);
    // Acquire Token
Future<AuthenticationResult> result = context.acquireToken(
            resourceUrl,
         new ClientCredential(APP_ID, APP_SECRET),
    null
    );
String token = result.get().getAccessToken();
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet("https://management.azure.com/subscriptions?api-version=2016-06-01");
request.addHeader("Authorization",result.get().getAccessTokenType()+ " "+ result.get().getAccessToken());
HttpResponse response = client.execute(request);
HttpEntity entity = response.getEntity();
// Read the contents of an entity and return it as a String.
String content = EntityUtils.toString(entity);
System.out.println(content);

Test Result:

Update2:

How to switch the directory with Azure portal.

Try it with List subscriptions Rest API directly.

Question:

What is the way to connect to azure ad with username and password using java I use python to print all users of azure directory but couldn't find a way to connect using username and password in java...

from azure.common.credentials import UserPassCredentials 
from azure.graphrbac import GraphRbacManagementClient

credentials = UserPassCredentials("username", "password", resource="https://graph.windows.net")

tenant_id = 'tenant id'
graphrbac_client = GraphRbacManagementClient(credentials,tenant_id)
users = graphrbac_client.users.list()
for user in users:
    print(f"Display name={user.display_name}UserPrincipleName={user.user_principal_name}")

the task is to print all users in the directory by making connection with username and password


Answer:

Microsoft Graph supports all functionality exposed by Azure AD Graph, and Microsoft Graph is recommended.

Install the dependency via Gradle:

Add the repository and a compile dependency for microsoft-graph-auth to your project's build.gradle

repository {
    jcenter()
    jcenter{
        url 'http://oss.jfrog.org/artifactory/oss-snapshot-local'
    }
}

dependency {
    // Include the sdk as a dependency
    compile('com.microsoft.graph:microsoft-graph-auth:0.1.0-SNAPSHOT')
}

Here is a working sample for your reference:

public static void main(String [] rags){
        UsernamePasswordProvider authProvider = new UsernamePasswordProvider("{client_id}", Arrays.asList("https://graph.microsoft.com/User.Read.All") , "{username}", "{password}", NationalCloud.Global, "{tenant}", "{cient_secret}");
        IGraphServiceClient graphClient = GraphServiceClient
                .builder()
                .authenticationProvider(authProvider)
                .buildClient();
        IUserCollectionPage userCollectionPage = graphClient.users().buildRequest().get();
        List<User> userList=userCollectionPage.getCurrentPage();
    }

Note: Remember to add the Microsoft graph api permission int the portal and grant admin consent.

Reference:

msgraph-sdk-java-auth