-
Notifications
You must be signed in to change notification settings - Fork 20
401 when using ChunkedUploadProvider for large mail attachments #52
Description
Expected behavior
ChunkedUploadProvider.upload(...) uploads the specified file and attaches it to the message, per https://docs.microsoft.com/en-us/graph/outlook-large-attachments?tabs=java.
Actual behavior
401 : Unauthorized upon request.
com.microsoft.graph.core.ClientException: Error code: InvalidAudienceForResource
Error message: The audience claim value is invalid for current resource. Audience claim is 'https://graph.microsoft.com/', request url is 'https://outlook.office.com/api/v2.0/Users...'.
The issue appears to be the API is including an Authorization header in the request, in contradiction with the documentation.
Do not specify an Authorization request header. The PUT query uses a pre-authenticated URL from the uploadUrl property, that allows access to the https://outlook.office.com domain.
Inspection of the authtoken parameter in the upload session's URL shows the correct audience ( "aud": "https://outlook.office.com/api/"), while the auth bearer token's audience is "https://graph.microsoft.com".
Steps to reproduce the behavior
Using an MS Dev account and pregenerated users; microsoft-graph-1.9.0.jar, microsoft-graph-core.1.0.1.jar, microsoft-graph-auth-1.9.0.jar; Java 8.
public class LargeAttachmentUploadError implements GraphExampleConstants {
public static void main(String[] args) throws IOException {
String recipient = args[0];
String filepath = args[1];
Path p = Paths.get(new File(filepath).toURI());
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(
clientID,
Arrays.asList("https://graph.microsoft.com/.default"),
username,
password,
NationalCloud.Global,
tenant,
clientSecret);
IGraphServiceClient graphClient = GraphServiceClient
.builder()
.authenticationProvider(authProvider)
.buildClient();
graphClient.getLogger().setLoggingLevel(LoggerLevel.DEBUG);
Message message = new Message();
message.subject = "Meet for lunch?";
ItemBody body = new ItemBody();
body.contentType = BodyType.TEXT;
body.content = "The new cafeteria is open.";
message.body = body;
LinkedList<Recipient> toRecipientsList = new LinkedList<Recipient>();
Recipient toRecipients = new Recipient();
EmailAddress emailAddress = new EmailAddress();
emailAddress.address = recipient;
toRecipients.emailAddress = emailAddress;
toRecipientsList.add(toRecipients);
message.toRecipients = toRecipientsList;
message.hasAttachments = true;
message = graphClient.me().mailFolders("drafts").messages().buildRequest().post(message);
AttachmentItem attachmentItem = new AttachmentItem();
attachmentItem.name = p.getFileName().toString();
attachmentItem.isInline = false;
attachmentItem.attachmentType = AttachmentType.FILE;
attachmentItem.size = Files.size(p);
UploadSession uploadSession = graphClient.me().messages(message.id).attachments().createUploadSession(attachmentItem).buildRequest().post();
try(InputStream inputStream = Files.newInputStream(p)) {
ChunkedUploadProvider<AttachmentItem> chunkedUploadProvider = new ChunkedUploadProvider<>(uploadSession, graphClient, inputStream, attachmentItem.size, AttachmentItem.class);
IProgressCallback<AttachmentItem> progressCallback = new IProgressCallback<AttachmentItem> () {
@Override
// Called after each slice of the file is uploaded
public void progress(final long current, final long max) {
System.out.println(String.format("Uploaded %d bytes of %d total bytes", current, max));
}
@Override
public void success(final AttachmentItem result) {
System.out.println(String.format("Uploaded mail attachment with ID: %s", result.name));
}
@Override
public void failure(ClientException ex) {
System.out.println("Error uploading mail attachment: " + ex.getMessage());
throw ex;
}
};
chunkedUploadProvider.upload(progressCallback, new int[] {320 * 1024, 1});
} catch (Exception e) {
e.printStackTrace();
}
}
}
AB#5829