aws / aws/aws-sdk-java-v2

Add native S3 Presigned POST URL support to S3Client

Open
#6,577 12 comments 24 reactions 0 assignees View on GitHub
feature-request
Dominant language
Java
Stars
2.6k
Forks
1k
Avg merge
2d 9h
Merged PRs (30d)
51

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)
```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)
```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`

```java
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 containing policy, signature, etc.
```

### Option 2: Add to `S3Utilities`

```java
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 formFields = result.formFields();
```

## Implementation Considerations

**Policy Document Structure:**
```json
{
"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 document
- `x-amz-algorithm`: Signing algorithm
- `x-amz-credential`: Credential scope
- `x-amz-date`: Request date
- `x-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](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html).

### Other Information

_No response_

### Acknowledgements

- [x] 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

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.