Hot questions for Using Azure in azure api management

Top Java Programmings / Azure / azure api management

Question:

I am newbie to Azure AD and want to interact with it through my java app. After doing some research, I found that we need to get bearer_token in order to use Graph API for Azure AD.

I am following this link to get bearer token but facing issue with one of parameters.

Now as shown in below image from above link, there are several parameters and information related to them is given like what they are and how to retrieve them but I dont see any information related 'code' parameter.

Can somebody tell me what is this 'code' and how am I supposed to get it?

Note: I have free trial account of Azure AD.

Any help is much appreciated!

Regards, Amit


Answer:

You are trying to use Authorization Code Grant Flow. You can read in detail about the flow and steps here in Microsoft Docs

It's a two step process:

STEP 1: Get Authorization Code by hitting the /authorize endpoint. You will get an authorization_code back as response for this call. Example shown below:

// Line breaks for legibility only

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&state=12345

STEP 2: Once you have an authorization_code from previous call, you can redeem it for an access token. Example shown below:

// Line breaks for legibility only

POST /{tenant}/oauth2/v2.0/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded

client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&client_secret=JqQX2PNo9bpM0uEihUPzyrh    // NOTE: Only required for web apps

Question:

I try to follow instructions here (azure API reference) to manage Azure API Management through their API.

Looks like that (groovy):

import groovy.json.JsonSlurper
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.HttpClientBuilder

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

@Grab(group = 'org.apache.httpcomponents', module = 'httpclient', version = '4.5.2')

final def serviceName = 'my-api'
final def url = "https://${serviceName}.management.azure-api.net"
final String identifier = 'integration'
final byte[] primaryKey = Base64.decoder.decode('<key copy pasted from Azure web console > "API Management Service"')

final String expiry = '2018-03-01T12:26:00.0000000Z'

// SAS generation
def hmacSha256 = Mac.getInstance("HmacSHA256")
hmacSha256.init(new SecretKeySpec(primaryKey, "HmacSHA256"))
def toSign = "$identifier\n$expiry"
def signature = new String(Base64.encoder.encode(hmacSha256.doFinal(toSign.bytes)))
def sas = "SharedAccessSignature uid=${identifier}&ex=$expiry&sn=${signature}"

// URL Request
def getUsers = new HttpGet("$url/users?api-version=2017-03-01")
getUsers.setHeader('Authorization', sas)

def client = HttpClientBuilder.create().build()
def response = client.execute(getUsers)

println response
if (response.statusLine.statusCode == 200) {
    println "Users: " + new JsonSlurper().parse(response.entity.content)
} else {
    println "Error: ${response.entity.content.readLines()}"
}

Which result with:

HttpResponseProxy{HTTP/1.1 401 Unauthorized [Content-Length: 0, Strict-Transport-Security: max-age=31536000; includeSubDomains, WWW-Authenticate: SharedAccessSignature realm="", error="invalid_token", error_description="User is not found or signature is invalid.", Date: Wed, 14 Feb 2018 14:33:14 GMT] [Content-Length: 0,Chunked: false]}

Note: when I'm using a manually generated API, it does work. The issue is on the signature generation.

Does anyone can give me some direction or working code sample (in Java)?


Answer:

For those having same issue and are lucky enough to find this answer, 2 issues:

  • signing algo is HmacSHA512, not HmacSHA256
  • primaryKey is not to be Base64 decoded. Just use it as it.

Working code (groovy):

import groovy.json.JsonSlurper
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.HttpClientBuilder

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

@Grab(group = 'org.apache.httpcomponents', module = 'httpclient', version = '4.5.2')

final def serviceName = '<your service name>'
final def url = "https://${serviceName}.management.azure-api.net"
final String identifier = '<your identifier>'
final byte[] primaryKey = '<copy paste of primaryKey>'.bytes // do not base64 decode!!!

final String expiry = LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'0000Z'"))

// SAS generation
def hmacSha512 = Mac.getInstance("HmacSHA512")
hmacSha512.init(new SecretKeySpec(primaryKey, "HmacSHA512"))
def dataToSign = "$identifier\n$expiry"
def signature = new String(Base64.encoder.encode(hmacSha512.doFinal(dataToSign.bytes)))
def sas = "SharedAccessSignature uid=${identifier}&ex=$expiry&sn=${signature}"
println "SAS=$sas"

// URL Request
def getUsers = new HttpGet("$url/users?api-version=2017-03-01")
getUsers.setHeader('Authorization', sas)

def client = HttpClientBuilder.create().build()
def response = client.execute(getUsers)

println response
if (response.statusLine.statusCode == 200) {
    println "Users: " + new JsonSlurper().parse(response.entity.content)
} else {
    println "Error: ${response.entity.content.readLines()}"
}

Question:

Issues while using client.listContainers() and container.listBlobs() functions and getting below JAVA exception. Tried to access them individually with reference methods and it works fine. Not sure why this is happening as there are no access restrictions i.e public access and getting the client reference without any issues of Connection String.


Exception:

java.util.NoSuchElementException: An error occurred while enumerating the result, check the original exception for details.
java.util.NoSuchElementException: An error occurred while enumerating the result, check the original exception for details.
at com.microsoft.azure.storage.core.LazySegmentedIterator.hasNext(LazySegmentedIterator.java:113)
at com.company.test.core.handler.AzureHeirarchyGenerator.getAzureFileList(AzureHeirarchyGenerator.java:69)
at com.company.test.core.handler.AzureHeirarchyGenerator.main(AzureHeirarchyGenerator.java:18)
Caused by: com.microsoft.azure.storage.StorageException: An unknown failure occurred : Connection refused: connect
at com.microsoft.azure.storage.StorageException.translateException(StorageException.java:66)
at com.microsoft.azure.storage.core.ExecutionEngine.executeWithRetry(ExecutionEngine.java:209)
at com.microsoft.azure.storage.core.LazySegmentedIterator.hasNext(LazySegmentedIterator.java:109)
... 2 more
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:673)
at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:558)
at sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264)
at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1156)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1564)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:347)
at com.microsoft.azure.storage.core.ExecutionEngine.executeWithRetry(ExecutionEngine.java:115)
... 3 more

Code Snippet JAVA:

    CloudStorageAccount account = CloudStorageAccount.parse(azureConnectionString);
    CloudBlobClient client = account.createCloudBlobClient();
    // List all containers of a storage account
    Iterable<CloudBlobContainer> containers = client.listContainers();
    for (CloudBlobContainer cloudBlobContainer : containers) {
        Iterable<ListBlobItem> blobs = cloudBlobContainer.lisBlobs();
        System.out.println("Code to fetch blobs inside container");
    }

Answer:

I have tested your code and it worked fine in my environment as below:

Here is my full code for your reference:

import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.ListBlobItem;

public class Main {

    public static void main(String[] args) {
    // TODO Auto-generated method stub

    CloudStorageAccount account;
    try {
        account = CloudStorageAccount.parse("DefaultEndpointsProtocol=https;AccountName=leeliublob;AccountKey=O7xxxxxxxxxxxxxxx/x/e9l9FhLqayXcbz1R+E0mIcJ5Wjkly1DsQPYY5dF2JrAVHtBozbJo29ZrrGJA==;EndpointSuffix=core.windows.net");
        CloudBlobClient client = account.createCloudBlobClient();

        Iterable<CloudBlobContainer> containers = client.listContainers();
        for (CloudBlobContainer cloudBlobContainer : containers) {
            Iterable<ListBlobItem> blobs = cloudBlobContainer.listBlobs();
            System.out.println("BlobContainer : "+ cloudBlobContainer.getName());
            System.out.println("   blobs inside container '"+cloudBlobContainer.getName()+"' are: ");
            int x=1;
            for (ListBlobItem blobItem : cloudBlobContainer.listBlobs()) {
                String blobName = blobItem.getUri().toString();
                System.out.println("         Number "+x+": url="+ blobItem.getUri().toString());
            x++;
            }

        }
    } catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (URISyntaxException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }



    }

}

I guess there may be two reasons which cause your errors:

  1. Please ensure that the connection string is correct.

  2. Please ensure to use the latest Java SDK and the SDK of Java which i used from : azure-storage-java