Add native S3 Presigned POST URL support to S3Client
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 38/100
Research direction
Start by reading the S3Presigner and S3Utilities entry points, then compare their signing flow with the S3 RESTObjectPOST API reference. The design still needs an agreed API between the proposed options; done means generating a presigned POST URL and form fields with policy conditions and AWS Signature Version 4 signing.
Written by the indexing model from the issue text.
Description
Describe the feature
Description
The AWS SDK for Java V2 should provide native support for generating presigned POST URLs with policy documents directly in the S3Client or S3Presigner, eliminating the need for workarounds or external implementations.
Background
This feature has been requested since 2019 in issue #1493, but remains unimplemented. Currently, developers must resort to workarounds such as:
- Calling AWS Lambda functions written in other languages (JavaScript/Python)
- Implementing custom signing logic
- Using third-party libraries
The V1 SDK also lacked this feature (aws/aws-sdk-java#834), making this a long-standing gap in the Java SDK ecosystem.
Justification
1. Feature Parity with Other SDKs
- Python (boto3): Provides
s3.generate_presigned_post()with policy conditions - JavaScript: Supports
createPresignedPost()with field policies - Go: Includes
PresignPostObject()functionality
2. Security & Best Practices
Presigned POST URLs with policy documents enable:
- Fine-grained access control without exposing AWS credentials
- Content-type validation to prevent malicious uploads
- File size limits to prevent abuse
- Key prefix restrictions to enforce folder structure
Related Issues
- #1493 - Original feature request (2019)
- #203 - Related presigning discussion
- aws/aws-sdk-java#834 - V1 SDK request
I Would Like to Implement This
I am currently implementing S3 POST support in S3 Ninja (an S3-compatible test server) and would be happy to contribute this feature to the SDK. Please assign this issue to me if accepted.
Having this feature would greatly benefit:
- Developers building modern web applications with direct browser uploads
- Teams testing S3 integrations locally with tools like S3 Ninja, LocalStack, or MinIO
- Organizations requiring fine-grained upload control without credential exposure
Use Case
Browser-based direct uploads to S3 are a standard pattern in modern web applications, requiring:
- Pre-flight authorization checks on the backend
- Secure, time-limited upload URLs
- Policy enforcement (file size, content type, key prefix restrictions)
Use Case Example
Backend (Java)
@PostMapping("/upload/presign")
public UploadCredentials getUploadUrl(@RequestBody UploadRequest request) {
// Validate user permissions
if (!authService.canUpload(request.getUserId())) {
throw new ForbiddenException();
}
// Generate presigned POST
PresignedPostResponse postResponse = s3Presigner.presignPost(req -> req
.bucket("user-uploads")
.key("users/" + request.getUserId() + "/${filename}")
.expiration(Instant.now().plus(Duration.ofMinutes(15)))
.conditions(conditions -> conditions
.contentLengthRange(1024, 5_242_880) // 1KB-5MB
.startsWith("key", "users/" + request.getUserId())
.eq("Content-Type", request.getContentType())));
return new UploadCredentials(
postResponse.url(),
postResponse.formFields()
);
}
Frontend (JavaScript)
// Get presigned POST from backend
const { url, fields } = await fetch('/upload/presign', {
method: 'POST',
body: JSON.stringify({ userId: '12345', contentType: 'image/jpeg' })
}).then(r => r.json());
// Upload directly to S3
const formData = new FormData();
Object.entries(fields).forEach(([key, value]) => {
formData.append(key, value);
});
formData.append('file', fileInput.files[0]);
await fetch(url, { method: 'POST', body: formData });
Proposed Solution
Proposed API
Option 1: Extend S3Presigner
S3Presigner presigner = S3Presigner.create();
PresignedPostRequest postRequest = PresignedPostRequest.builder()
.bucket("my-bucket")
.key("uploads/${filename}")
.expiration(Instant.now().plus(Duration.ofMinutes(15)))
.conditions(PolicyConditions.builder()
.contentLengthRange(1024, 10_485_760) // 1KB to 10MB
.startsWith("key", "uploads/")
.eq("Content-Type", "image/jpeg")
.eq("x-amz-meta-user-id", "12345")
.build())
.build();
PresignedPostResponse response = presigner.presignPost(postRequest);
// Returns:
// - URL: https://my-bucket.s3.amazonaws.com/
// - Form fields: Map<String, String> containing policy, signature, etc.
Option 2: Add to S3Utilities
S3Utilities utilities = S3Utilities.builder()
.region(Region.US_EAST_1)
.credentialsProvider(credentialsProvider)
.build();
PostSigningResult result = utilities.signPostRequest(request -> request
.bucket("my-bucket")
.key("uploads/${filename}")
.expirationTime(Instant.now().plus(Duration.ofMinutes(15)))
.addCondition("content-length-range", 1024, 10_485_760)
.addCondition("starts-with", "$key", "uploads/")
.addCondition("eq", "$Content-Type", "image/jpeg"));
String url = result.url();
Map<String, String> formFields = result.formFields();
Implementation Considerations
Policy Document Structure:
{
"expiration": "2024-12-01T12:00:00.000Z",
"conditions": [
{"bucket": "my-bucket"},
["starts-with", "$key", "uploads/"],
{"Content-Type": "image/jpeg"},
["content-length-range", 1024, 10485760],
{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
{"x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20241121/us-east-1/s3/aws4_request"},
{"x-amz-date": "20241121T000000Z"}
]
}
Form Fields to Return:
key: Object key (can include${filename}placeholder)policy: Base64-encoded policy documentx-amz-algorithm: Signing algorithmx-amz-credential: Credential scopex-amz-date: Request datex-amz-signature: Calculated signature- Any additional fields from policy conditions
Signature Calculation:
Should follow AWS Signature Version 4 for POST requests as documented in the S3 API Reference.
Other Information
No response
Acknowledgements
- I may be able to implement this feature request
- This feature might incur a breaking change
AWS Java SDK version used
2
JDK version used
24
Operating System and version
Windows 11
- Dominant language
- Java
- Stars
- 2.6k
- Forks
- 1k
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 39
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from aws/aws-sdk-java-v2
-
bug needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
aws/aws-sdk-java-v2#7379 ·
-
bug needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
aws/aws-sdk-java-v2#7174 ·
-
bug needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
aws/aws-sdk-java-v2#7060 ·
-
announcement
Difficulty 3/5 1-2 days Newbie friendliness 25/100
aws/aws-sdk-java-v2#7385 ·
-
bug
aws/aws-sdk-java-v2#7350 · 4 comments · 1 assignee ·
All issues in aws/aws-sdk-java-v2
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
infinispan/infinispan#18150 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
opensearch-project/k-NN#3597 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100