-
Notifications
You must be signed in to change notification settings - Fork 965
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
- Support Presigned POST URL Generator with policy document #1493 - Original feature request (2019)
- Presigned URL Generators #203 - Related presigning discussion
- Presigned V4 URLs using Java SDK 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