Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/formrecognizer/azure-ai-formrecognizer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Release History

## 1.0.0-beta.3 (Unreleased)

- Added AAD authentication support
Comment thread
mssfang marked this conversation as resolved.
Outdated

## 1.0.0-beta.2 (2020-05-06)
- Fixed Receipt type bug to select the valueString field via fieldValue.
Expand Down
73 changes: 56 additions & 17 deletions sdk/formrecognizer/azure-ai-formrecognizer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,25 @@ az cognitiveservices account create \
--yes
```
### Authenticate the client
In order to interact with the Form Recognizer service, you will need to create an instance of the `FormRecognizerClient`
class. You will need an **endpoint** and an **key** to instantiate a client object,
In order to interact with the Form Recognizer service, you will need to create an instance of the Form Recognizer client,
Comment thread
mssfang marked this conversation as resolved.
Outdated
both the asynchronous and synchronous clients can be created by using `FormRecognizerClientBuilder` invoking `buildClient()`
Comment thread
mssfang marked this conversation as resolved.
Outdated
creates a synchronous client while `buildAsyncClient` creates its asynchronous counterpart.

You will need an **endpoint** and a **key** to instantiate a client object,
they can be found in the [Azure Portal][azure_portal] under the "Quickstart" in your created
Form Recognizer resource. See the full details regarding [authentication][authentication] of Cognitive Services.

#### Get credentials
The `credential` parameter may be provided as a [`AzureKeyCredential`][azure_key_credential] from [azure-core][azure_core].

##### Create FormRecognizerClient with AzureKeyCredential
To use AzureKeyCredential authentication, provide the [key][key] as a string to the [AzureKeyCredential][azure_key_credential]. This can be found in the [Azure Portal][azure_portal]
under the "Quickstart" section or by running the following Azure CLI command:
#### Create a Form Recognizer client using AzureKeyCredential
To use `AzureKeyCredential` authentication, provide the [key][key] as a string to the [AzureKeyCredential][azure_key_credential].
This key can be found in the [Azure Portal][azure_portal] under the "Quickstart" section in your created Form Recognizer
Comment thread
mssfang marked this conversation as resolved.
Outdated
resource, or by running the following Azure CLI command to get the key from the Form Recognizer resource:

```bash
az cognitiveservices account keys list --resource-group <your-resource-group-name> --name <your-resource-name>
```
Use the API key as the credential parameter to authenticate the client:
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L37-L40 -->

Use the key as the credential parameter to authenticate the client:
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L39-L42 -->
```java
FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder()
.credential(new AzureKeyCredential("{key}"))
Expand All @@ -83,7 +85,7 @@ FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder()
```
The Azure Form Recognizer client library provides a way to **rotate the existing API key**.
Comment thread
mssfang marked this conversation as resolved.
Outdated

<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L47-L53 -->
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L61-L67 -->
```java
AzureKeyCredential credential = new AzureKeyCredential("{key}");
FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder()
Expand All @@ -94,6 +96,41 @@ FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder()
credential.update("{new_key}");
```

#### Create a Form Recognizer client using Microsoft identity platform (formerly Azure Active Directory)

Azure SDK for Java supports an Azure Identity package, making it easy to get credentials from Microsoft identity
platform. First, add the package:

[//]: # ({x-version-update-start;com.azure:azure-identity;current})
Comment thread
mssfang marked this conversation as resolved.
Outdated
```xml
<dependency>
<groupId>com.azure</groupId>
Comment thread
mssfang marked this conversation as resolved.
<artifactId>azure-identity</artifactId>
<version>1.0.3</version>
</dependency>
```
[//]: # ({x-version-update-end})

All the implemented ways to request a credential can be found under the `com.azure.identity.credential` package. The
Comment thread
mssfang marked this conversation as resolved.
Outdated
sample below shows how to use an Azure Active Directory (AAD) application client secret to authorize with Azure Form
Recognizer.

##### Authorizing with DefaultAzureCredential
Comment thread
mssfang marked this conversation as resolved.
Outdated

Authorization is easiest using [DefaultAzureCredential][wiki_identity]. It finds the best credential to use in its
running environment. For more information about using Azure Active Directory authorization with Form Recognizer, please
refer to [the associated documentation][aad_authorization].

<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L49-L54 -->
```java
TokenCredential credential = new DefaultAzureCredentialBuilder()
.build();
FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder()
.endpoint("{endpoint}")
.credential(credential)
.buildClient();
```

Comment thread
mssfang marked this conversation as resolved.
## Key concepts
### FormRecognizerClient
The [FormRecognizerClient][form_recognizer_sync_client] and [FormRecognizerAsyncClient][form_recognizer_async_client]
Expand Down Expand Up @@ -143,7 +180,7 @@ The following section provides several code snippets covering some of the most c
### Recognize Forms Using a Custom Model
Recognize name/value pairs and table data from forms. These models are trained with your own data,
so they're tailored to your forms. You should only recognize forms of the same form type that the custom model was trained on.
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L57-L73 -->
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L71-L87 -->
```java
String analyzeFilePath = "{file_source_url}";
String modelId = "{custom_trained_model_id}";
Expand All @@ -166,7 +203,7 @@ recognizedForms.forEach(form -> {

### Recognize Content
Recognize text and table structures, along with their bounding box coordinates, from documents.
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L77-L97 -->
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L91-L111 -->
```java
String analyzeFilePath = "{file_source_url}";
SyncPoller<OperationResult, IterableStream<FormPage>> recognizeLayoutPoller =
Expand All @@ -193,7 +230,7 @@ layoutPageResults.forEach(formPage -> {

### Recognize receipts
Recognize data from a USA sales receipts using a prebuilt model.
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L101-L118 -->
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L115-L132 -->
```java
String receiptSourceUrl = "https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/media"
+ "/contoso-allinone.jpg";
Expand All @@ -219,7 +256,7 @@ receiptPageResults.forEach(recognizedReceipt -> {
Train a machine-learned model on your own form type. The resulting model will be able to recognize values from the types of forms it was trained on.
Provide a container SAS url to your Azure Storage Blob container where you're storing the training documents. See details on setting this up
in the [service quickstart documentation][quickstart_training].
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L122-L142 -->
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L136-L156 -->
```java
String trainingSetSource = "{unlabeled_training_set_SAS_URL}";
SyncPoller<OperationResult, CustomFormModel> trainingPoller =
Expand All @@ -246,7 +283,7 @@ customFormModel.getSubModels().forEach(customFormSubModel -> {

### Manage your models
Manage the custom models attached to your account.
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L146-L175 -->
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L160-L189 -->
```java
AtomicReference<String> modelId = new AtomicReference<>();
// First, we see how many custom models we have, and what our limit is
Expand Down Expand Up @@ -288,7 +325,7 @@ to provide an invalid file source URL an `ErrorResponseException` would be raise
In the following code snippet, the error is handled
gracefully by catching the exception and display the additional information about the error.

<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L182-L186 -->
<!-- embedme ./src/samples/java/com/azure/ai/formrecognizer/ReadmeSamples.java#L196-L200 -->
```java
try {
formRecognizerClient.beginRecognizeContentFromUrl("invalidSourceUrl");
Expand Down Expand Up @@ -336,6 +373,7 @@ When you submit a pull request, a CLA-bot will automatically determine whether y
This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments.

<!-- LINKS -->
[aad_authorization]: https://docs.microsoft.com/en-us/azure/cognitive-services/authentication#authenticate-with-azure-active-directory
Comment thread
mssfang marked this conversation as resolved.
Outdated
[azure_key_credential]: https://github.com/Azure/azure-sdk-for-java/blob/master/sdk/core/azure-core/src/main/java/com/azure/core/credential/AzureKeyCredential.java
[key]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account?tabs=multiservice%2Cwindows#get-the-keys-for-your-resource
[api_reference_doc]: https://aka.ms/azsdk-java-formrecognizer-ref-docs
Expand Down Expand Up @@ -385,5 +423,6 @@ This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For m
[service_doc_train_labeled]: https://docs.microsoft.com/en-us/azure/cognitive-services/form-recognizer/overview#train-with-labels
[source_code]: src
[quickstart_training]: https://docs.microsoft.com/azure/cognitive-services/form-recognizer/quickstarts/curl-train-extract#train-a-form-recognizer-model
[wiki_identity]: https://github.com/Azure/azure-sdk-for-java/wiki/Identity-and-Authentication

![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-java%2Fsdk%2Fformrecognizer%2Fazure-ai-formrecognizer%2FREADME.png)
6 changes: 6 additions & 0 deletions sdk/formrecognizer/azure-ai-formrecognizer/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,11 @@
<version>5.4.2</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-params;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.0.6</version> <!-- {x-version-update;com.azure:azure-identity;dependency} -->
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.azure.ai.formrecognizer.implementation.FormRecognizerClientImplBuilder;
import com.azure.core.annotation.ServiceClientBuilder;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.credential.TokenCredential;
import com.azure.core.http.ContentType;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpHeaders;
Expand All @@ -15,6 +16,7 @@
import com.azure.core.http.policy.AddDatePolicy;
import com.azure.core.http.policy.AddHeadersPolicy;
import com.azure.core.http.policy.AzureKeyCredentialPolicy;
import com.azure.core.http.policy.BearerTokenAuthenticationPolicy;
import com.azure.core.http.policy.HttpLogDetailLevel;
import com.azure.core.http.policy.HttpLogOptions;
import com.azure.core.http.policy.HttpLoggingPolicy;
Expand Down Expand Up @@ -43,7 +45,7 @@
*
* <p>
* The client needs the service endpoint of the Azure Form Recognizer to access the resource service.
* {@link #credential(AzureKeyCredential)} gives
* {@link #credential(AzureKeyCredential)} or {@link #credential(TokenCredential) credential(TokenCredential)} gives
* the builder access credential.
* </p>
*
Expand All @@ -68,6 +70,7 @@ public final class FormRecognizerClientBuilder {
private static final String NAME = "name";
private static final String VERSION = "version";
private static final RetryPolicy DEFAULT_RETRY_POLICY = new RetryPolicy("retry-after-ms", ChronoUnit.MILLIS);
private static final String DEFAULT_SCOPE = "https://cognitiveservices.azure.com/.default";

private final ClientLogger logger = new ClientLogger(FormRecognizerClientBuilder.class);
private final List<HttpPipelinePolicy> policies;
Expand All @@ -82,6 +85,7 @@ public final class FormRecognizerClientBuilder {
private HttpPipeline httpPipeline;
private Configuration configuration;
private RetryPolicy retryPolicy;
private TokenCredential tokenCredential;
private FormRecognizerServiceVersion version;

static final String OCP_APIM_SUBSCRIPTION_KEY = "Ocp-Apim-Subscription-Key";
Expand Down Expand Up @@ -173,7 +177,9 @@ private HttpPipeline getDefaultHttpPipeline(Configuration buildConfiguration) {
policies.add(retryPolicy == null ? DEFAULT_RETRY_POLICY : retryPolicy);
policies.add(new AddDatePolicy());
// Authentications
if (credential != null) {
if (tokenCredential != null) {
policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, DEFAULT_SCOPE));
} else if (credential != null) {
policies.add(new AzureKeyCredentialPolicy(OCP_APIM_SUBSCRIPTION_KEY, credential));
} else {
// Throw exception that credential and tokenCredential cannot be null
Expand Down Expand Up @@ -232,6 +238,19 @@ public FormRecognizerClientBuilder credential(AzureKeyCredential apiKeyCredentia
return this;
}

/**
* Sets the {@link TokenCredential} used to authenticate HTTP requests.
*
* @param tokenCredential {@link TokenCredential} used to authenticate HTTP requests.
* @return The updated {@link FormRecognizerClientBuilder} object.
* @throws NullPointerException If {@code tokenCredential} is {@code null}.
*/
public FormRecognizerClientBuilder credential(TokenCredential tokenCredential) {
Objects.requireNonNull(tokenCredential, "'tokenCredential' cannot be null.");
this.tokenCredential = tokenCredential;
Comment thread
mssfang marked this conversation as resolved.
Outdated
return this;
}

/**
* Sets the logging configuration for HTTP requests and responses.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
import com.azure.ai.formrecognizer.models.RecognizedReceipt;
import com.azure.ai.formrecognizer.models.USReceipt;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.credential.TokenCredential;
import com.azure.core.http.rest.PagedIterable;
import com.azure.core.util.IterableStream;
import com.azure.core.util.polling.SyncPoller;
import com.azure.identity.DefaultAzureCredentialBuilder;

import java.util.concurrent.atomic.AtomicReference;

Expand All @@ -40,6 +42,18 @@ public void useAzureKeyCredentialSyncClient() {
.buildClient();
}

/**
* Code snippet for getting async client using AAD authentication.
*/
public void useAadAsyncClient() {
TokenCredential credential = new DefaultAzureCredentialBuilder()
.build();
Comment thread
mssfang marked this conversation as resolved.
Outdated
FormRecognizerClient formRecognizerClient = new FormRecognizerClientBuilder()
.endpoint("{endpoint}")
.credential(credential)
.buildClient();
}

/**
* Code snippet for rotating AzureKeyCredential of the client
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.ai.formrecognizer;

import com.azure.ai.formrecognizer.models.CustomFormModelInfo;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.http.HttpClient;
import com.azure.core.test.TestBase;
import com.azure.core.util.Configuration;
import com.azure.identity.DefaultAzureCredentialBuilder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static com.azure.ai.formrecognizer.FormTrainingClientTestBase.AZURE_FORM_RECOGNIZER_API_KEY;
import static com.azure.ai.formrecognizer.FormTrainingClientTestBase.AZURE_FORM_RECOGNIZER_ENDPOINT;
import static com.azure.ai.formrecognizer.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Test for using Azure Active Directory token credential.
*/
public class AadAuthenticationTest extends TestBase {
private static FormTrainingClient client;

private void setup(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) {
final String endpoint = getEndpoint();
if (interceptorManager.isPlaybackMode()) {
// In playback mode use connection string because CI environment doesn't set up to support AAD
Comment thread
mssfang marked this conversation as resolved.
Outdated
client = new FormRecognizerClientBuilder()
.credential(new AzureKeyCredential(getApiKey()))
.endpoint(endpoint)
.httpClient(interceptorManager.getPlaybackClient())
.buildClient().getFormTrainingClient();
} else {
client = new FormRecognizerClientBuilder()
.httpClient(httpClient)
.credential(new DefaultAzureCredentialBuilder().build())
.endpoint(endpoint)
.addPolicy(interceptorManager.getRecordPolicy()) // Record
.serviceVersion(serviceVersion)
.buildClient().getFormTrainingClient();
}
}

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("com.azure.ai.formrecognizer.TestUtils#getTestParameters")
public void aadAuthenticationTest(HttpClient httpClient, FormRecognizerServiceVersion serviceVersion) {
Comment thread
mssfang marked this conversation as resolved.
Outdated
setup(httpClient, serviceVersion);
for (CustomFormModelInfo modelInfo : client.getModelInfos()) {
assertTrue(modelInfo.getModelId() != null && modelInfo.getCreatedOn() != null
&& modelInfo.getLastUpdatedOn() != null && modelInfo.getStatus() != null);
}
}

private String getApiKey() {
return interceptorManager.isPlaybackMode() ? "apiKeyInPlayback"
: Configuration.getGlobalConfiguration().get(AZURE_FORM_RECOGNIZER_API_KEY);
}

private String getEndpoint() {
return interceptorManager.isPlaybackMode() ? "https://localhost:8080"
: Configuration.getGlobalConfiguration().get(AZURE_FORM_RECOGNIZER_ENDPOINT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package com.azure.ai.formrecognizer;

import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.credential.TokenCredential;
import org.junit.jupiter.api.Test;

import static com.azure.ai.formrecognizer.TestUtils.VALID_HTTPS_LOCALHOST;
Expand Down Expand Up @@ -52,9 +53,22 @@ public void invalidProtocol() {
*/
@Test
public void nullAzureKeyCredential() {
AzureKeyCredential azureKeyCredential = null;
assertThrows(NullPointerException.class, () -> {
final FormRecognizerClientBuilder builder = new FormRecognizerClientBuilder();
builder.endpoint(VALID_HTTPS_LOCALHOST).credential(null);
builder.endpoint(VALID_HTTPS_LOCALHOST).credential(azureKeyCredential);
});
}

/**
* Test for null AAD credential
*/
@Test
public void nullAADCredential() {
TokenCredential tokenCredential = null;
assertThrows(NullPointerException.class, () -> {
final FormRecognizerClientBuilder builder = new FormRecognizerClientBuilder();
builder.endpoint(VALID_HTTPS_LOCALHOST).credential(tokenCredential);
});
}

Expand Down
Loading