SlideShare a Scribd company logo
AWS User Group: Dublin Meetup / 2022-11-17
1
META_SLIDE!
fth.link/presign
loige 2
😎"I have a startup idea..."
loige
fth.link/presign
3
loige
meower
fth.link/presign
4
loige 5
loige 6
loige 7
loige
WTF?
8
Yes, I still have to implement the
profile picture upload feature... 😅
loige 9
$ ~ whoami
👋I'm Luciano ( 🍕🍝 )
Senior Architect @ fourTheorem (Dublin )
nodejsdp.link
📔Co-Author of Node.js Design Patterns 👉
Let's connect!
(blog)
(twitter)
(twitch)
(github)
loige.co
@loige
loige
lmammino 10
Always re-imagining
We are a pioneering technology consultancy
focused on AWS and serverless
| |
Accelerated Serverless AI as a Service Platform Modernisation
loige
✉Reach out to us at
😇We are always looking for talent:
hello@fourTheorem.com
fth.link/careers
11
We host a weekly podcast about AWS
awsbites.com
loige 12
🤔
"upload" feature
in a web app...
loige 13
What's an upload, really?
loige 14
OK, what protocol?
loige 15
loige
Structure of an HTTP request
POST /profilepic/upload HTTP/1.1
Host: api.meower.com
Content-Type: text/plain
Content-Length: 9
Some data
Method
Path Version
Headers
Body
16
loige
What if it's a binary (like a picture)?
PUT /profilepic/upload HTTP/1.1
Host: api.meower.com
Content-Type: image/jpeg
Content-Length: 2097852
����JFIFHH������"��
���Dl��FW�'6N�()H�'p��FD3 [...]
read 2097852
bytes
17
loige
Using Lambda
Lambda proxy integration*
* JSON Based protocol mapping to HTTP
(JSON Request / JSON Response)
18
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "POST",
"headers": {
"Content-Type": "text/plain",
"Content-Length": "9",
"Host": "api.meower.com",
},
"body": "Some data"
}
1
2
3
4
5
6
7
8
9
10
11
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
{
1
2
3
"httpMethod": "POST",
4
"headers": {
5
"Content-Type": "text/plain",
6
"Content-Length": "9",
7
"Host": "api.meower.com",
8
},
9
"body": "Some data"
10
}
11
"httpMethod": "POST",
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
4
"headers": {
5
"Content-Type": "text/plain",
6
"Content-Length": "9",
7
"Host": "api.meower.com",
8
},
9
"body": "Some data"
10
}
11
"headers": {
"Content-Type": "text/plain",
"Content-Length": "9",
"Host": "api.meower.com",
},
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "POST",
4
5
6
7
8
9
"body": "Some data"
10
}
11
"body": "Some data"
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "POST",
4
"headers": {
5
"Content-Type": "text/plain",
6
"Content-Length": "9",
7
"Host": "api.meower.com",
8
},
9
10
}
11
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "POST",
"headers": {
"Content-Type": "text/plain",
"Content-Length": "9",
"Host": "api.meower.com",
},
"body": "Some data"
}
1
2
3
4
5
6
7
8
9
10
11
loige
Lambda proxy integration (request)
19
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "PUT",
"headers": {
"Content-Type": "image/jpeg",
"Content-Length": "2097852",
"Host": "api.meower.com",
},
"body": "????????"
}
1
2
3
4
5
6
7
8
9
10
11
"Content-Type": "image/jpeg",
"Content-Length": "2097852",
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "PUT",
4
"headers": {
5
6
7
"Host": "api.meower.com",
8
},
9
"body": "????????"
10
}
11
"body": "????????"
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "PUT",
4
"headers": {
5
"Content-Type": "image/jpeg",
6
"Content-Length": "2097852",
7
"Host": "api.meower.com",
8
},
9
10
}
11
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "PUT",
"headers": {
"Content-Type": "image/jpeg",
"Content-Length": "2097852",
"Host": "api.meower.com",
},
"body": "????????"
}
1
2
3
4
5
6
7
8
9
10
11
loige
Lambda proxy integration (request - picture)
20
"isBase64Encoded": true,
"body": "/9j/4AAQSkZJRgABAQEASABIAAD/2w[...]"
{
1
"resource": "/profilepic/upload",
2
"path": "/profilepic/upload",
3
"httpMethod": "PUT",
4
"headers": {
5
"Content-Type": "image/jpeg",
6
"Content-Length": "2097852",
7
"Host": "api.meower.com",
8
},
9
10
11
}
12
{
"resource": "/profilepic/upload",
"path": "/profilepic/upload",
"httpMethod": "PUT",
"headers": {
"Content-Type": "image/jpeg",
"Content-Length": "2097852",
"Host": "api.meower.com",
},
"isBase64Encoded": true,
"body": "/9j/4AAQSkZJRgABAQEASABIAAD/2w[...]"
}
1
2
3
4
5
6
7
8
9
10
11
12
loige
Lambda proxy integration (request - picture)
21
loige
1. Parse request (JSON)
2. Decode body (Base64)
3. Validation / resize
4. ...
Lambda proxy integration request
/profilepic/upload
22
loige
1. Parse request (JSON)
2. Decode body (Base64)
3. Validation / resize
4. ...
Lambda proxy integration request
/profilepic/upload
😎 23
loige
Is this a good solution? 🙄
24
loige
Limitations...
API Gateway requests timeout: 30 sec
API Gateway payload: max 10 MB
Lambda timeout: max 15 mins
Lambda payload size: max 6 MB
Upload: 6 MB / 30 sec
25
loige
Is this a good solution? 🙄
... not really!
What about supporting big images or even videos?
26
loige
An alternative approach
✅ Long lived connection
✅ No size limit
27
loige
🤔SERVERS...
28
loige
S3 pre-signed URLs 😱
An S3 built-in feature
to authorize operations (download, upload, etc) on a
bucket / object
using time-limited authenticated URLs
29
loige
Using S3 pre-signed URLs for upload
* yeah, this can be a Lambda as well 😇
*
30
loige
Using S3 pre-signed URLs for upload
* yeah, this can be a Lambda as well 😇
*
31
loige
Using S3 pre-signed URLs for upload
* yeah, this can be a Lambda as well 😇
*
32
loige
Using S3 pre-signed URLs for upload
* yeah, this can be a Lambda as well 😇
*
33
loige
Using S3 pre-signed URLs for upload
✅
* yeah, this can be a Lambda as well 😇
*
34
loige
... and we can also use it for downloads! 🤩
35
loige
Using S3 pre-signed URLs for download
36
loige
Using S3 pre-signed URLs for download
37
loige
Using S3 pre-signed URLs for download
38
loige
Using S3 pre-signed URLs for download
39
loige
Using S3 pre-signed URLs for download
✅
40
loige
⚠VERY important details!
I lied to you a little in those diagrams... 🤥
It's a decent mental model, but it's not accurate 😅
The server never really talks with S3!
The server actually creates the signed URL by itself!
We will see later what's the security model around this idea!
41
loige
Is this a good solution? 🙄
✅It's a managed feature (a.k.a. no servers to manage)
✅We can upload and download arbitrarily big files with no practical limits*
✅Reasonably simple and secure
👍Seems good to me!
* objects in S3 are "limited" to 5TB (when using multi-part upload), 5 GB otherwise.
42
loige
Generating our first pre-signed URL
$ aws s3 presign 
s3://finance-department-bucket/2022/tax-certificate.pdf
https://s3.amazonaws.com/finance-department-bucket/2022/tax-
certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-
Credential=AKIA3SGQVQG7FGA6KKA6%2F20221104%2Fus-east-
1%2Fs3%2Faws4_request&X-Amz-Date=20221104T140227Z&X-Amz-
Expires=3600&X-Amz-SignedHeaders=host&X-Amz-
Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4751b4d9787
314fd6da4d55
Whoever has this URL can
download the tax certificate!
43
loige
What's in a pre-signed URL
https://s3.amazonaws.com/finance-department-bucket/2022/tax-
certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-
Credential=AKIA3SGQVQG7FGA6KKA6%2F20221104%2Fus-east-
1%2Fs3%2Faws4_request&X-Amz-Date=20221104T140227Z&X-Amz-
Expires=3600&X-Amz-SignedHeaders=host&X-Amz-
Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4751b4d9787
314fd6da4d55
44
loige
What's in a pre-signed URL
https://s3.amazonaws.com
/finance-department-bucket
/2022/tax-certificate.pdf
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIA3SGQXQG7XXXYKKA6%2F20221104...
&X-Amz-Date=20221104T140227Z
&X-Amz-Expires=3600
&X-Amz-SignedHeaders=host
&X-Amz-Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4...
What if I change this to
/passwords.txt?
45
loige
👿46
loige
Pre-signed URLs validation
https://s3.amazonaws.com
/finance-department-bucket
/2022/tax-certificate.pdf
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIA3SGQXQG7XXXYKKA6%2F20221104...
&X-Amz-Date=20221104T140227Z
&X-Amz-Expires=3600
&X-Amz-SignedHeaders=host
&X-Amz-Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4...
47
loige
🤓
Once a pre-signed URL is generated you
cannot edit it without breaking it
Photo by on
CHUTTERSNAP Unsplash
⚠Also note that you can use a pre-signed URL as many
times as you want until it expires
48
loige
🔐Permissions
Anyone with valid credentials can create a pre-signed URL (client side)
valid credentials = Role, User, or Security Token
The generated URL inherits the permissions of the credentials used to generate it
This means you can generate pre-signed URLs for things you don't have access to 😅
49
loige
$ aws s3 presign s3://ireland/i-love-you
https://ireland.s3.eu-west-1.amazonaws.com/i-love-you?X-Amz-
Algorithm=AWS4-HMAC-SHA256&X-Amz-
Credential=AKIA3ABCVQG7FGA6KKA6%2F20221115%2Feu-west-
1%2Fs3%2Faws4_request&X-Amz-Date=20221115T182036Z&X-Amz-
Expires=3600&X-Amz-SignedHeaders=host&X-Amz-
Signature=75749c92d94d03e411e7bbf64419f2af09301d1791b0df54c639
137c715f7888
😱
I swear I don't even know if this
bucket exists or who owns it!
50
loige
Pre-signed URLs are validated at request time
51
loige
Creating a pre-signed URL
programmatically
AWS SDK for JavaScript v3
52
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const s3Client = new S3Client()
const command = new GetObjectCommand({
Bucket: "some-bucket",
Key: "some-object"
})
const preSignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600
})
console.log(preSignedUrl)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
1
2
3
const s3Client = new S3Client()
4
5
const command = new GetObjectCommand({
6
Bucket: "some-bucket",
7
Key: "some-object"
8
})
9
10
const preSignedUrl = await getSignedUrl(s3Client, command, {
11
expiresIn: 3600
12
})
13
14
console.log(preSignedUrl)
15
const s3Client = new S3Client()
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
1
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2
3
4
5
const command = new GetObjectCommand({
6
Bucket: "some-bucket",
7
Key: "some-object"
8
})
9
10
const preSignedUrl = await getSignedUrl(s3Client, command, {
11
expiresIn: 3600
12
})
13
14
console.log(preSignedUrl)
15
const command = new GetObjectCommand({
Bucket: "some-bucket",
Key: "some-object"
})
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
1
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2
3
const s3Client = new S3Client()
4
5
6
7
8
9
10
const preSignedUrl = await getSignedUrl(s3Client, command, {
11
expiresIn: 3600
12
})
13
14
console.log(preSignedUrl)
15
const preSignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600
})
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
1
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2
3
const s3Client = new S3Client()
4
5
const command = new GetObjectCommand({
6
Bucket: "some-bucket",
7
Key: "some-object"
8
})
9
10
11
12
13
14
console.log(preSignedUrl)
15 console.log(preSignedUrl)
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
1
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
2
3
const s3Client = new S3Client()
4
5
const command = new GetObjectCommand({
6
Bucket: "some-bucket",
7
Key: "some-object"
8
})
9
10
const preSignedUrl = await getSignedUrl(s3Client, command, {
11
expiresIn: 3600
12
})
13
14
15
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const s3Client = new S3Client()
const command = new GetObjectCommand({
Bucket: "some-bucket",
Key: "some-object"
})
const preSignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600
})
console.log(preSignedUrl)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
loige 53
loige
📦
Uploading a file
using pre-signed URLs
54
loige
2 Options: PUT & POST 🤨
55
loige
PUT Method
PUT <preSignedURL> HTTP/1.1
Host: <bucket>.s3.<region>.amazonaws.com
Content-Length: 2097852
����JFIFHH������"��
���Dl��FW�'6N�()H�'p��FD3 [...]
56
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const s3Client = new S3Client()
const command = new PutObjectCommand({
Bucket: "some-bucket",
Key: "some-object"
})
const preSignedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600
})
console.log(preSignedUrl)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
loige
Only difference with the
previous example
57
loige
PUT Method - Limitations
You cannot set a limit on the upload size (max of 5 GB)! *
You can limit the Content-Type but you can specify exactly one
* Unless you know the exact size in advance
58
loige
POST method
It uses the multipart/form-data encoding (form upload)
Gives more freedom to the client to shape the request (Content-Type, file name, etc)
It uses a policy mechanism to define the "rules" of what can be uploaded
E.g. you can limit the supported mime types and provide a maximum file size
You can use it to upload from a web form and even configure the redirect URL
It's not really a URL but more of a pre-signed form!
59
POST / HTTP/1.1
Host: <bucket>.s3.amazonaws.com
Content-Type: multipart/form-data; boundary=9431149156168
Content-Length: 2097852
--9431149156168
Content-Disposition: form-data; name="key"
picture.jpg
--9431149156168
Content-Disposition: form-data; name="X-Amz-Credential"
AKIA3SGABCDXXXA6KKA6/20221115/eu-west-1/s3/aws4_request
--9431149156168
Content-Disposition: form-data; name="Policy"
eyJleHBpcmF0aW9uIjoiMjAyMi0xMS0xNVQyMDo0NjozN1oiLCJjb25kaXRpb25zIjpbWyJj[...]
--9431149156168
Content-Disposition: form-data; name="X-Amz-Signature"
2c1da0001dfec7caea1c9fb80c7bc8847f515a9e4483d2942464f48d2f827de7
--9431149156168
Content-Disposition: form-data; name="file"; filename="MyFilename.jpg"
Content-Type: image/jpeg
����JFIFHH������"��
���Dl��FW�'6N�()H�'p��FD3[...]
--9431149156168--
loige
60
loige
POST method Policy
A JSON object (Base64 encoded) that defines the upload rules (conditions) and the
expiration date
This is what gets signed: you cannot alter the policy without breaking the signature
{
"expiration": "2022-11-15T20:46:37Z",
"conditions": [
["content-length-range", 0, 5242880],
["starts-with", "$Content-Type", "image/"],
{"bucket": "somebucket"},
{"X-Amz-Algorithm": "AWS4-HMAC-SHA256"},
{"X-Amz-Credential": "AKIA3SGABCDXXXA6KKA6/20221115/eu-west-1/s3/aws4_request"},
{"X-Amz-Date": "20221115T194637Z"},
{"key": "picture.jpg"}
]
}
61
import { S3Client } from '@aws-sdk/client-s3'
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
const { BUCKET_NAME, OBJECT_KEY } = process.env
const s3Client = new S3Client()
const { url, fields } = await createPresignedPost(s3Client, {
Bucket: 'somebucket',
Key: 'someobject',
Conditions: [
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
],
Fields: {
success_action_status: '201',
'Content-Type': 'image/png'
},
Expires: 3600
})
console.log({ url, fields })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
import { S3Client } from '@aws-sdk/client-s3'
1
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
const { url, fields } = await createPresignedPost(s3Client, {
})
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
18
19
console.log({ url, fields })
20
Bucket: 'somebucket',
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
Key: 'someobject',
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
Conditions: [
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
],
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
10
11
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
Fields: {
success_action_status: '201',
'Content-Type': 'image/png'
},
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
13
14
15
16
Expires: 3600
17
})
18
19
console.log({ url, fields })
20
Expires: 3600
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
17
})
18
19
console.log({ url, fields })
20 console.log({ url, fields })
import { S3Client } from '@aws-sdk/client-s3'
1
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
2
3
const { BUCKET_NAME, OBJECT_KEY } = process.env
4
const s3Client = new S3Client()
5
6
const { url, fields } = await createPresignedPost(s3Client, {
7
Bucket: 'somebucket',
8
Key: 'someobject',
9
Conditions: [
10
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
11
],
12
Fields: {
13
success_action_status: '201',
14
'Content-Type': 'image/png'
15
},
16
Expires: 3600
17
})
18
19
20
import { S3Client } from '@aws-sdk/client-s3'
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
const { BUCKET_NAME, OBJECT_KEY } = process.env
const s3Client = new S3Client()
const { url, fields } = await createPresignedPost(s3Client, {
Bucket: 'somebucket',
Key: 'someobject',
Conditions: [
['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max
],
Fields: {
success_action_status: '201',
'Content-Type': 'image/png'
},
Expires: 3600
})
console.log({ url, fields })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
loige 62
// you can use `url` and `fields` to generate an HTML form
const code = `<h1>Upload an image to S3</h1>
<form action="${url}" method="post" enctype="multipart/form-data">
${Object.entries(fields).map(([key, value]) => {
return `<input type="hidden" name="${key}" value="${value.replace(/"/g, '&quot;')}">`
}).join('n')}
<div><input type="file" name="file" accept="image/png"></div>
<div><input type="submit" value="Upload"></div>
</form>`
1
2
3
4
5
6
7
8
9
10
loige 63
loige
Limitations and quirks
It supports only 1 file (cannot upload multiple files in one go)
The file field must be the last entry in the form
(S3 will ignore every other field after the file)
From the browser (AJAX) you need to enable CORS on the bucket
64
loige
Should I use PUT or POST? 🧐
PUT is simpler but definitely more limited
POST is slightly more complicated (and less adopted) but it's more flexible
You should probably put some time into learning POST and use that!
65
loige
Pre-signed URLs for other operations
S3 pre-signed URLs are not limited to GET, PUT or POST operations
You can literally create pre-signed URLs for any command
(DeleteObject, ListBuckets, MultiPartUpload, etc...)
66
loige
Do you need moar examples? 😼
github.com/lmammino/s3-presigned-urls-examples
67
loige
... In summary
S3 pre-signed URLs are a great way to authorise operations on S3
They are generally used to implement upload/download features
The signature is created client-side so you can sign anything. Access is validated at
request time
This is not the only solution, you can also use the JavaScript SDK from the frontend
and get limited credentials from Cognito (Amplify makes that process simpler)
For upload you can use PUT and POST, but POST is much more flexible
💬PS: Meower.com doesn't really exist... but... do you want to invest?! It's a great
idea, trust me!
68
Cover photo by on
Kelly Sikkema Unsplash
fourtheorem.com
THANKS! 🙌
fth.link/presign
loige
It's a wrap!
69

More Related Content

What's hot

실전 서버 부하테스트 노하우
실전 서버 부하테스트 노하우 실전 서버 부하테스트 노하우
실전 서버 부하테스트 노하우 YoungSu Son
 
Introduction of Apache Camel
Introduction of Apache CamelIntroduction of Apache Camel
Introduction of Apache CamelKnoldus Inc.
 
ProxySQL Cluster - Percona Live 2022
ProxySQL Cluster - Percona Live 2022ProxySQL Cluster - Percona Live 2022
ProxySQL Cluster - Percona Live 2022René Cannaò
 
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기YongSung Yoon
 
[2017 AWS Startup Day] AWS 비용 최대 90% 절감하기: 스팟 인스턴스 Deep-Dive
[2017 AWS Startup Day] AWS 비용 최대 90% 절감하기: 스팟 인스턴스 Deep-Dive [2017 AWS Startup Day] AWS 비용 최대 90% 절감하기: 스팟 인스턴스 Deep-Dive
[2017 AWS Startup Day] AWS 비용 최대 90% 절감하기: 스팟 인스턴스 Deep-Dive Amazon Web Services Korea
 
[135] 오픈소스 데이터베이스, 은행 서비스에 첫발을 내밀다.
[135] 오픈소스 데이터베이스, 은행 서비스에 첫발을 내밀다.[135] 오픈소스 데이터베이스, 은행 서비스에 첫발을 내밀다.
[135] 오픈소스 데이터베이스, 은행 서비스에 첫발을 내밀다.NAVER D2
 
프론트엔드 개발자가 혼자 AWS 기반 웹애플리케이션 만들기::박찬민::AWS Summit Seoul 2018
프론트엔드 개발자가 혼자 AWS 기반 웹애플리케이션 만들기::박찬민::AWS Summit Seoul 2018프론트엔드 개발자가 혼자 AWS 기반 웹애플리케이션 만들기::박찬민::AWS Summit Seoul 2018
프론트엔드 개발자가 혼자 AWS 기반 웹애플리케이션 만들기::박찬민::AWS Summit Seoul 2018Amazon Web Services Korea
 
국내 미디어 고객사의 AWS 활용 사례 - POOQ서비스 그리고 마이크로서비스 아키텍처, 콘텐츠연합플랫폼 - 박명순부장, 콘텐츠연합플랫폼 ...
국내 미디어 고객사의 AWS 활용 사례 - POOQ서비스 그리고 마이크로서비스 아키텍처, 콘텐츠연합플랫폼 - 박명순부장, 콘텐츠연합플랫폼 ...국내 미디어 고객사의 AWS 활용 사례 - POOQ서비스 그리고 마이크로서비스 아키텍처, 콘텐츠연합플랫폼 - 박명순부장, 콘텐츠연합플랫폼 ...
국내 미디어 고객사의 AWS 활용 사례 - POOQ서비스 그리고 마이크로서비스 아키텍처, 콘텐츠연합플랫폼 - 박명순부장, 콘텐츠연합플랫폼 ...Amazon Web Services Korea
 
kotlinx.serialization
kotlinx.serializationkotlinx.serialization
kotlinx.serializationArawn Park
 
Going Serverless with CQRS on AWS
Going Serverless with CQRS on AWSGoing Serverless with CQRS on AWS
Going Serverless with CQRS on AWSAnton Udovychenko
 
AWS 기반 클라우드 아키텍처 모범사례 - 삼성전자 개발자 포털/개발자 워크스페이스 - 정영준 솔루션즈 아키텍트, AWS / 유현성 수석,...
AWS 기반 클라우드 아키텍처 모범사례 - 삼성전자 개발자 포털/개발자 워크스페이스 - 정영준 솔루션즈 아키텍트, AWS / 유현성 수석,...AWS 기반 클라우드 아키텍처 모범사례 - 삼성전자 개발자 포털/개발자 워크스페이스 - 정영준 솔루션즈 아키텍트, AWS / 유현성 수석,...
AWS 기반 클라우드 아키텍처 모범사례 - 삼성전자 개발자 포털/개발자 워크스페이스 - 정영준 솔루션즈 아키텍트, AWS / 유현성 수석,...Amazon Web Services Korea
 
ProxySQL on Kubernetes
ProxySQL on KubernetesProxySQL on Kubernetes
ProxySQL on KubernetesRené Cannaò
 
Nodejs Explained with Examples
Nodejs Explained with ExamplesNodejs Explained with Examples
Nodejs Explained with ExamplesGabriele Lana
 
Drools 6.0 (Red Hat Summit)
Drools 6.0 (Red Hat Summit)Drools 6.0 (Red Hat Summit)
Drools 6.0 (Red Hat Summit)Mark Proctor
 
Amazon VPC와 ELB/Direct Connect/VPN 알아보기 - 김세준, AWS 솔루션즈 아키텍트
Amazon VPC와 ELB/Direct Connect/VPN 알아보기 - 김세준, AWS 솔루션즈 아키텍트Amazon VPC와 ELB/Direct Connect/VPN 알아보기 - 김세준, AWS 솔루션즈 아키텍트
Amazon VPC와 ELB/Direct Connect/VPN 알아보기 - 김세준, AWS 솔루션즈 아키텍트Amazon Web Services Korea
 
20분안에 스타트업이 알아야하는 AWS의 모든것 - 윤석찬 :: 스타트업얼라이언스 런치클럽
20분안에 스타트업이 알아야하는 AWS의 모든것 - 윤석찬 :: 스타트업얼라이언스 런치클럽20분안에 스타트업이 알아야하는 AWS의 모든것 - 윤석찬 :: 스타트업얼라이언스 런치클럽
20분안에 스타트업이 알아야하는 AWS의 모든것 - 윤석찬 :: 스타트업얼라이언스 런치클럽Amazon Web Services Korea
 
Amazon & AWS의 MSA와 DevOps, 그리고 지속적 혁신
Amazon & AWS의 MSA와 DevOps, 그리고 지속적 혁신Amazon & AWS의 MSA와 DevOps, 그리고 지속적 혁신
Amazon & AWS의 MSA와 DevOps, 그리고 지속적 혁신AgileKoreaConference Alliance
 
마이크로서비스 기반 클라우드 아키텍처 구성 모범 사례 - 윤석찬 (AWS 테크에반젤리스트)
마이크로서비스 기반 클라우드 아키텍처 구성 모범 사례 - 윤석찬 (AWS 테크에반젤리스트) 마이크로서비스 기반 클라우드 아키텍처 구성 모범 사례 - 윤석찬 (AWS 테크에반젤리스트)
마이크로서비스 기반 클라우드 아키텍처 구성 모범 사례 - 윤석찬 (AWS 테크에반젤리스트) Amazon Web Services Korea
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJSAbul Hasan
 

What's hot (20)

실전 서버 부하테스트 노하우
실전 서버 부하테스트 노하우 실전 서버 부하테스트 노하우
실전 서버 부하테스트 노하우
 
Introduction of Apache Camel
Introduction of Apache CamelIntroduction of Apache Camel
Introduction of Apache Camel
 
ProxySQL Cluster - Percona Live 2022
ProxySQL Cluster - Percona Live 2022ProxySQL Cluster - Percona Live 2022
ProxySQL Cluster - Percona Live 2022
 
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
[Spring Camp 2018] 11번가 Spring Cloud 기반 MSA로의 전환 : 지난 1년간의 이야기
 
[2017 AWS Startup Day] AWS 비용 최대 90% 절감하기: 스팟 인스턴스 Deep-Dive
[2017 AWS Startup Day] AWS 비용 최대 90% 절감하기: 스팟 인스턴스 Deep-Dive [2017 AWS Startup Day] AWS 비용 최대 90% 절감하기: 스팟 인스턴스 Deep-Dive
[2017 AWS Startup Day] AWS 비용 최대 90% 절감하기: 스팟 인스턴스 Deep-Dive
 
[135] 오픈소스 데이터베이스, 은행 서비스에 첫발을 내밀다.
[135] 오픈소스 데이터베이스, 은행 서비스에 첫발을 내밀다.[135] 오픈소스 데이터베이스, 은행 서비스에 첫발을 내밀다.
[135] 오픈소스 데이터베이스, 은행 서비스에 첫발을 내밀다.
 
프론트엔드 개발자가 혼자 AWS 기반 웹애플리케이션 만들기::박찬민::AWS Summit Seoul 2018
프론트엔드 개발자가 혼자 AWS 기반 웹애플리케이션 만들기::박찬민::AWS Summit Seoul 2018프론트엔드 개발자가 혼자 AWS 기반 웹애플리케이션 만들기::박찬민::AWS Summit Seoul 2018
프론트엔드 개발자가 혼자 AWS 기반 웹애플리케이션 만들기::박찬민::AWS Summit Seoul 2018
 
국내 미디어 고객사의 AWS 활용 사례 - POOQ서비스 그리고 마이크로서비스 아키텍처, 콘텐츠연합플랫폼 - 박명순부장, 콘텐츠연합플랫폼 ...
국내 미디어 고객사의 AWS 활용 사례 - POOQ서비스 그리고 마이크로서비스 아키텍처, 콘텐츠연합플랫폼 - 박명순부장, 콘텐츠연합플랫폼 ...국내 미디어 고객사의 AWS 활용 사례 - POOQ서비스 그리고 마이크로서비스 아키텍처, 콘텐츠연합플랫폼 - 박명순부장, 콘텐츠연합플랫폼 ...
국내 미디어 고객사의 AWS 활용 사례 - POOQ서비스 그리고 마이크로서비스 아키텍처, 콘텐츠연합플랫폼 - 박명순부장, 콘텐츠연합플랫폼 ...
 
kotlinx.serialization
kotlinx.serializationkotlinx.serialization
kotlinx.serialization
 
Going Serverless with CQRS on AWS
Going Serverless with CQRS on AWSGoing Serverless with CQRS on AWS
Going Serverless with CQRS on AWS
 
AWS 기반 클라우드 아키텍처 모범사례 - 삼성전자 개발자 포털/개발자 워크스페이스 - 정영준 솔루션즈 아키텍트, AWS / 유현성 수석,...
AWS 기반 클라우드 아키텍처 모범사례 - 삼성전자 개발자 포털/개발자 워크스페이스 - 정영준 솔루션즈 아키텍트, AWS / 유현성 수석,...AWS 기반 클라우드 아키텍처 모범사례 - 삼성전자 개발자 포털/개발자 워크스페이스 - 정영준 솔루션즈 아키텍트, AWS / 유현성 수석,...
AWS 기반 클라우드 아키텍처 모범사례 - 삼성전자 개발자 포털/개발자 워크스페이스 - 정영준 솔루션즈 아키텍트, AWS / 유현성 수석,...
 
ProxySQL on Kubernetes
ProxySQL on KubernetesProxySQL on Kubernetes
ProxySQL on Kubernetes
 
Nodejs Explained with Examples
Nodejs Explained with ExamplesNodejs Explained with Examples
Nodejs Explained with Examples
 
Drools 6.0 (Red Hat Summit)
Drools 6.0 (Red Hat Summit)Drools 6.0 (Red Hat Summit)
Drools 6.0 (Red Hat Summit)
 
Amazon VPC와 ELB/Direct Connect/VPN 알아보기 - 김세준, AWS 솔루션즈 아키텍트
Amazon VPC와 ELB/Direct Connect/VPN 알아보기 - 김세준, AWS 솔루션즈 아키텍트Amazon VPC와 ELB/Direct Connect/VPN 알아보기 - 김세준, AWS 솔루션즈 아키텍트
Amazon VPC와 ELB/Direct Connect/VPN 알아보기 - 김세준, AWS 솔루션즈 아키텍트
 
JavaScript Event Loop
JavaScript Event LoopJavaScript Event Loop
JavaScript Event Loop
 
20분안에 스타트업이 알아야하는 AWS의 모든것 - 윤석찬 :: 스타트업얼라이언스 런치클럽
20분안에 스타트업이 알아야하는 AWS의 모든것 - 윤석찬 :: 스타트업얼라이언스 런치클럽20분안에 스타트업이 알아야하는 AWS의 모든것 - 윤석찬 :: 스타트업얼라이언스 런치클럽
20분안에 스타트업이 알아야하는 AWS의 모든것 - 윤석찬 :: 스타트업얼라이언스 런치클럽
 
Amazon & AWS의 MSA와 DevOps, 그리고 지속적 혁신
Amazon & AWS의 MSA와 DevOps, 그리고 지속적 혁신Amazon & AWS의 MSA와 DevOps, 그리고 지속적 혁신
Amazon & AWS의 MSA와 DevOps, 그리고 지속적 혁신
 
마이크로서비스 기반 클라우드 아키텍처 구성 모범 사례 - 윤석찬 (AWS 테크에반젤리스트)
마이크로서비스 기반 클라우드 아키텍처 구성 모범 사례 - 윤석찬 (AWS 테크에반젤리스트) 마이크로서비스 기반 클라우드 아키텍처 구성 모범 사례 - 윤석찬 (AWS 테크에반젤리스트)
마이크로서비스 기반 클라우드 아키텍처 구성 모범 사례 - 윤석찬 (AWS 테크에반젤리스트)
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJS
 

Similar to Everything I know about S3 pre-signed URLs

Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS FargateBuilding a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargatedatree
 
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...Amazon Web Services
 
Continuous Deployment @ AWS Re:Invent
Continuous Deployment @ AWS Re:InventContinuous Deployment @ AWS Re:Invent
Continuous Deployment @ AWS Re:InventJohn Schneider
 
There is something about serverless
There is something about serverlessThere is something about serverless
There is something about serverlessgjdevos
 
Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)Yan Cui
 
DevSecCon Singapore 2018 - Remove developers’ shameful secrets or simply rem...
DevSecCon Singapore 2018 -  Remove developers’ shameful secrets or simply rem...DevSecCon Singapore 2018 -  Remove developers’ shameful secrets or simply rem...
DevSecCon Singapore 2018 - Remove developers’ shameful secrets or simply rem...DevSecCon
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance ComputingLuciano Mammino
 
SnapyX
SnapyXSnapyX
SnapyXekino
 
Adopt openjdk and how it impacts you in 2020
Adopt openjdk and how it impacts you in 2020Adopt openjdk and how it impacts you in 2020
Adopt openjdk and how it impacts you in 2020George Adams
 
Gojko's 5 rules for super responsive Serverless applications
Gojko's 5 rules for super responsive Serverless applicationsGojko's 5 rules for super responsive Serverless applications
Gojko's 5 rules for super responsive Serverless applicationsDaniel Zivkovic
 
Serverless in Production, an experience report (AWS UG South Wales)
Serverless in Production, an experience report (AWS UG South Wales)Serverless in Production, an experience report (AWS UG South Wales)
Serverless in Production, an experience report (AWS UG South Wales)Yan Cui
 
DevSecCon SG 2018 Fabian Presentation Slides
DevSecCon SG 2018 Fabian Presentation SlidesDevSecCon SG 2018 Fabian Presentation Slides
DevSecCon SG 2018 Fabian Presentation SlidesFab L
 
Lessons learned from a large scale OSGi web app
Lessons learned from a large scale OSGi web appLessons learned from a large scale OSGi web app
Lessons learned from a large scale OSGi web appPaul Bakker
 
IDEALIZE 2023 - NodeJS & Firebase Session
IDEALIZE 2023 - NodeJS & Firebase SessionIDEALIZE 2023 - NodeJS & Firebase Session
IDEALIZE 2023 - NodeJS & Firebase SessionBrion Mario
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance ComputingLuciano Mammino
 
Serverless in production, an experience report
Serverless in production, an experience reportServerless in production, an experience report
Serverless in production, an experience reportYan Cui
 

Similar to Everything I know about S3 pre-signed URLs (20)

Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS FargateBuilding a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
Building a dev pipeline using GitHub Actions, Node.js, and AWS ECS Fargate
 
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
Continuous Integration and Deployment Best Practices on AWS (ARC307) | AWS re...
 
Continuous Deployment @ AWS Re:Invent
Continuous Deployment @ AWS Re:InventContinuous Deployment @ AWS Re:Invent
Continuous Deployment @ AWS Re:Invent
 
Supercharge your app with Cloud Functions for Firebase
Supercharge your app with Cloud Functions for FirebaseSupercharge your app with Cloud Functions for Firebase
Supercharge your app with Cloud Functions for Firebase
 
There is something about serverless
There is something about serverlessThere is something about serverless
There is something about serverless
 
Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)Serverless in production, an experience report (FullStack 2018)
Serverless in production, an experience report (FullStack 2018)
 
DevSecCon Singapore 2018 - Remove developers’ shameful secrets or simply rem...
DevSecCon Singapore 2018 -  Remove developers’ shameful secrets or simply rem...DevSecCon Singapore 2018 -  Remove developers’ shameful secrets or simply rem...
DevSecCon Singapore 2018 - Remove developers’ shameful secrets or simply rem...
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
 
SnapyX
SnapyXSnapyX
SnapyX
 
SnapyX - ParisJS
SnapyX - ParisJSSnapyX - ParisJS
SnapyX - ParisJS
 
[AWS Builders] Effective AWS Glue
[AWS Builders] Effective AWS Glue[AWS Builders] Effective AWS Glue
[AWS Builders] Effective AWS Glue
 
Adopt openjdk and how it impacts you in 2020
Adopt openjdk and how it impacts you in 2020Adopt openjdk and how it impacts you in 2020
Adopt openjdk and how it impacts you in 2020
 
Gojko's 5 rules for super responsive Serverless applications
Gojko's 5 rules for super responsive Serverless applicationsGojko's 5 rules for super responsive Serverless applications
Gojko's 5 rules for super responsive Serverless applications
 
Serverless in Production, an experience report (AWS UG South Wales)
Serverless in Production, an experience report (AWS UG South Wales)Serverless in Production, an experience report (AWS UG South Wales)
Serverless in Production, an experience report (AWS UG South Wales)
 
DevSecCon SG 2018 Fabian Presentation Slides
DevSecCon SG 2018 Fabian Presentation SlidesDevSecCon SG 2018 Fabian Presentation Slides
DevSecCon SG 2018 Fabian Presentation Slides
 
Lessons learned from a large scale OSGi web app
Lessons learned from a large scale OSGi web appLessons learned from a large scale OSGi web app
Lessons learned from a large scale OSGi web app
 
IDEALIZE 2023 - NodeJS & Firebase Session
IDEALIZE 2023 - NodeJS & Firebase SessionIDEALIZE 2023 - NodeJS & Firebase Session
IDEALIZE 2023 - NodeJS & Firebase Session
 
Function as a Service
Function as a ServiceFunction as a Service
Function as a Service
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
 
Serverless in production, an experience report
Serverless in production, an experience reportServerless in production, an experience report
Serverless in production, an experience report
 

More from Luciano Mammino

Did you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSDid you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSLuciano Mammino
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoBuilding an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoLuciano Mammino
 
From Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperFrom Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperLuciano Mammino
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Luciano Mammino
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022Luciano Mammino
 
Building an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableBuilding an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableLuciano Mammino
 
Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Luciano Mammino
 
A look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust DublinA look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust DublinLuciano Mammino
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaLuciano Mammino
 
A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)Luciano Mammino
 
AWS Observability Made Simple
AWS Observability Made SimpleAWS Observability Made Simple
AWS Observability Made SimpleLuciano Mammino
 
Semplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessSemplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessLuciano Mammino
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Luciano Mammino
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Luciano Mammino
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3Luciano Mammino
 
Finding a lost song with Node.js and async iterators
Finding a lost song with Node.js and async iteratorsFinding a lost song with Node.js and async iterators
Finding a lost song with Node.js and async iteratorsLuciano Mammino
 
AWS Observability (without the Pain)
AWS Observability (without the Pain)AWS Observability (without the Pain)
AWS Observability (without the Pain)Luciano Mammino
 

More from Luciano Mammino (20)

Did you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSDid you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJS
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoBuilding an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
 
From Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperFrom Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiper
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
 
Building an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableBuilding an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & Airtable
 
Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀
 
A look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust DublinA look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust Dublin
 
Monoliths to the cloud!
Monoliths to the cloud!Monoliths to the cloud!
Monoliths to the cloud!
 
The senior dev
The senior devThe senior dev
The senior dev
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community Vijayawada
 
A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)
 
AWS Observability Made Simple
AWS Observability Made SimpleAWS Observability Made Simple
AWS Observability Made Simple
 
Semplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessSemplificare l'observability per progetti Serverless
Semplificare l'observability per progetti Serverless
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3
 
Finding a lost song with Node.js and async iterators
Finding a lost song with Node.js and async iteratorsFinding a lost song with Node.js and async iterators
Finding a lost song with Node.js and async iterators
 
AWS Observability (without the Pain)
AWS Observability (without the Pain)AWS Observability (without the Pain)
AWS Observability (without the Pain)
 

Recently uploaded

10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka DoktorováCzechDreamin
 
Optimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through ObservabilityOptimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through ObservabilityScyllaDB
 
"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor TurskyiFwdays
 
Free and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
Free and Effective: Making Flows Publicly Accessible, Yumi IbrahimzadeFree and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
Free and Effective: Making Flows Publicly Accessible, Yumi IbrahimzadeCzechDreamin
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaRTTS
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Thierry Lestable
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...Sri Ambati
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfCheryl Hung
 
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlFuture Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlPeter Udo Diehl
 
Salesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone KomSalesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone KomCzechDreamin
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesThousandEyes
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersSafe Software
 
Speed Wins: From Kafka to APIs in Minutes
Speed Wins: From Kafka to APIs in MinutesSpeed Wins: From Kafka to APIs in Minutes
Speed Wins: From Kafka to APIs in Minutesconfluent
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backElena Simperl
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Tobias Schneck
 
Introduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG EvaluationIntroduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG EvaluationZilliz
 
IESVE for Early Stage Design and Planning
IESVE for Early Stage Design and PlanningIESVE for Early Stage Design and Planning
IESVE for Early Stage Design and PlanningIES VE
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualityInflectra
 
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...CzechDreamin
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Jeffrey Haguewood
 

Recently uploaded (20)

10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
 
Optimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through ObservabilityOptimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through Observability
 
"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi
 
Free and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
Free and Effective: Making Flows Publicly Accessible, Yumi IbrahimzadeFree and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
Free and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlFuture Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
 
Salesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone KomSalesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
Salesforce Adoption – Metrics, Methods, and Motivation, Antone Kom
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
Speed Wins: From Kafka to APIs in Minutes
Speed Wins: From Kafka to APIs in MinutesSpeed Wins: From Kafka to APIs in Minutes
Speed Wins: From Kafka to APIs in Minutes
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
Introduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG EvaluationIntroduction to Open Source RAG and RAG Evaluation
Introduction to Open Source RAG and RAG Evaluation
 
IESVE for Early Stage Design and Planning
IESVE for Early Stage Design and PlanningIESVE for Early Stage Design and Planning
IESVE for Early Stage Design and Planning
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
 

Everything I know about S3 pre-signed URLs

  • 1. AWS User Group: Dublin Meetup / 2022-11-17 1
  • 3. 😎"I have a startup idea..." loige fth.link/presign 3
  • 9. Yes, I still have to implement the profile picture upload feature... 😅 loige 9
  • 10. $ ~ whoami 👋I'm Luciano ( 🍕🍝 ) Senior Architect @ fourTheorem (Dublin ) nodejsdp.link 📔Co-Author of Node.js Design Patterns 👉 Let's connect! (blog) (twitter) (twitch) (github) loige.co @loige loige lmammino 10
  • 11. Always re-imagining We are a pioneering technology consultancy focused on AWS and serverless | | Accelerated Serverless AI as a Service Platform Modernisation loige ✉Reach out to us at 😇We are always looking for talent: hello@fourTheorem.com fth.link/careers 11
  • 12. We host a weekly podcast about AWS awsbites.com loige 12
  • 13. 🤔 "upload" feature in a web app... loige 13
  • 14. What's an upload, really? loige 14
  • 16. loige Structure of an HTTP request POST /profilepic/upload HTTP/1.1 Host: api.meower.com Content-Type: text/plain Content-Length: 9 Some data Method Path Version Headers Body 16
  • 17. loige What if it's a binary (like a picture)? PUT /profilepic/upload HTTP/1.1 Host: api.meower.com Content-Type: image/jpeg Content-Length: 2097852 ����JFIFHH������"�� ���Dl��FW�'6N�()H�'p��FD3 [...] read 2097852 bytes 17
  • 18. loige Using Lambda Lambda proxy integration* * JSON Based protocol mapping to HTTP (JSON Request / JSON Response) 18
  • 19. { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "POST", "headers": { "Content-Type": "text/plain", "Content-Length": "9", "Host": "api.meower.com", }, "body": "Some data" } 1 2 3 4 5 6 7 8 9 10 11 "resource": "/profilepic/upload", "path": "/profilepic/upload", { 1 2 3 "httpMethod": "POST", 4 "headers": { 5 "Content-Type": "text/plain", 6 "Content-Length": "9", 7 "Host": "api.meower.com", 8 }, 9 "body": "Some data" 10 } 11 "httpMethod": "POST", { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 4 "headers": { 5 "Content-Type": "text/plain", 6 "Content-Length": "9", 7 "Host": "api.meower.com", 8 }, 9 "body": "Some data" 10 } 11 "headers": { "Content-Type": "text/plain", "Content-Length": "9", "Host": "api.meower.com", }, { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "POST", 4 5 6 7 8 9 "body": "Some data" 10 } 11 "body": "Some data" { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "POST", 4 "headers": { 5 "Content-Type": "text/plain", 6 "Content-Length": "9", 7 "Host": "api.meower.com", 8 }, 9 10 } 11 { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "POST", "headers": { "Content-Type": "text/plain", "Content-Length": "9", "Host": "api.meower.com", }, "body": "Some data" } 1 2 3 4 5 6 7 8 9 10 11 loige Lambda proxy integration (request) 19
  • 20. { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "PUT", "headers": { "Content-Type": "image/jpeg", "Content-Length": "2097852", "Host": "api.meower.com", }, "body": "????????" } 1 2 3 4 5 6 7 8 9 10 11 "Content-Type": "image/jpeg", "Content-Length": "2097852", { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "PUT", 4 "headers": { 5 6 7 "Host": "api.meower.com", 8 }, 9 "body": "????????" 10 } 11 "body": "????????" { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "PUT", 4 "headers": { 5 "Content-Type": "image/jpeg", 6 "Content-Length": "2097852", 7 "Host": "api.meower.com", 8 }, 9 10 } 11 { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "PUT", "headers": { "Content-Type": "image/jpeg", "Content-Length": "2097852", "Host": "api.meower.com", }, "body": "????????" } 1 2 3 4 5 6 7 8 9 10 11 loige Lambda proxy integration (request - picture) 20
  • 21. "isBase64Encoded": true, "body": "/9j/4AAQSkZJRgABAQEASABIAAD/2w[...]" { 1 "resource": "/profilepic/upload", 2 "path": "/profilepic/upload", 3 "httpMethod": "PUT", 4 "headers": { 5 "Content-Type": "image/jpeg", 6 "Content-Length": "2097852", 7 "Host": "api.meower.com", 8 }, 9 10 11 } 12 { "resource": "/profilepic/upload", "path": "/profilepic/upload", "httpMethod": "PUT", "headers": { "Content-Type": "image/jpeg", "Content-Length": "2097852", "Host": "api.meower.com", }, "isBase64Encoded": true, "body": "/9j/4AAQSkZJRgABAQEASABIAAD/2w[...]" } 1 2 3 4 5 6 7 8 9 10 11 12 loige Lambda proxy integration (request - picture) 21
  • 22. loige 1. Parse request (JSON) 2. Decode body (Base64) 3. Validation / resize 4. ... Lambda proxy integration request /profilepic/upload 22
  • 23. loige 1. Parse request (JSON) 2. Decode body (Base64) 3. Validation / resize 4. ... Lambda proxy integration request /profilepic/upload 😎 23
  • 24. loige Is this a good solution? 🙄 24
  • 25. loige Limitations... API Gateway requests timeout: 30 sec API Gateway payload: max 10 MB Lambda timeout: max 15 mins Lambda payload size: max 6 MB Upload: 6 MB / 30 sec 25
  • 26. loige Is this a good solution? 🙄 ... not really! What about supporting big images or even videos? 26
  • 27. loige An alternative approach ✅ Long lived connection ✅ No size limit 27
  • 29. loige S3 pre-signed URLs 😱 An S3 built-in feature to authorize operations (download, upload, etc) on a bucket / object using time-limited authenticated URLs 29
  • 30. loige Using S3 pre-signed URLs for upload * yeah, this can be a Lambda as well 😇 * 30
  • 31. loige Using S3 pre-signed URLs for upload * yeah, this can be a Lambda as well 😇 * 31
  • 32. loige Using S3 pre-signed URLs for upload * yeah, this can be a Lambda as well 😇 * 32
  • 33. loige Using S3 pre-signed URLs for upload * yeah, this can be a Lambda as well 😇 * 33
  • 34. loige Using S3 pre-signed URLs for upload ✅ * yeah, this can be a Lambda as well 😇 * 34
  • 35. loige ... and we can also use it for downloads! 🤩 35
  • 36. loige Using S3 pre-signed URLs for download 36
  • 37. loige Using S3 pre-signed URLs for download 37
  • 38. loige Using S3 pre-signed URLs for download 38
  • 39. loige Using S3 pre-signed URLs for download 39
  • 40. loige Using S3 pre-signed URLs for download ✅ 40
  • 41. loige ⚠VERY important details! I lied to you a little in those diagrams... 🤥 It's a decent mental model, but it's not accurate 😅 The server never really talks with S3! The server actually creates the signed URL by itself! We will see later what's the security model around this idea! 41
  • 42. loige Is this a good solution? 🙄 ✅It's a managed feature (a.k.a. no servers to manage) ✅We can upload and download arbitrarily big files with no practical limits* ✅Reasonably simple and secure 👍Seems good to me! * objects in S3 are "limited" to 5TB (when using multi-part upload), 5 GB otherwise. 42
  • 43. loige Generating our first pre-signed URL $ aws s3 presign s3://finance-department-bucket/2022/tax-certificate.pdf https://s3.amazonaws.com/finance-department-bucket/2022/tax- certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz- Credential=AKIA3SGQVQG7FGA6KKA6%2F20221104%2Fus-east- 1%2Fs3%2Faws4_request&X-Amz-Date=20221104T140227Z&X-Amz- Expires=3600&X-Amz-SignedHeaders=host&X-Amz- Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4751b4d9787 314fd6da4d55 Whoever has this URL can download the tax certificate! 43
  • 44. loige What's in a pre-signed URL https://s3.amazonaws.com/finance-department-bucket/2022/tax- certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz- Credential=AKIA3SGQVQG7FGA6KKA6%2F20221104%2Fus-east- 1%2Fs3%2Faws4_request&X-Amz-Date=20221104T140227Z&X-Amz- Expires=3600&X-Amz-SignedHeaders=host&X-Amz- Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4751b4d9787 314fd6da4d55 44
  • 45. loige What's in a pre-signed URL https://s3.amazonaws.com /finance-department-bucket /2022/tax-certificate.pdf ?X-Amz-Algorithm=AWS4-HMAC-SHA256 &X-Amz-Credential=AKIA3SGQXQG7XXXYKKA6%2F20221104... &X-Amz-Date=20221104T140227Z &X-Amz-Expires=3600 &X-Amz-SignedHeaders=host &X-Amz-Signature=b228dbec8c1008c80c162e1210e4503dceead1e4d4... What if I change this to /passwords.txt? 45
  • 48. loige 🤓 Once a pre-signed URL is generated you cannot edit it without breaking it Photo by on CHUTTERSNAP Unsplash ⚠Also note that you can use a pre-signed URL as many times as you want until it expires 48
  • 49. loige 🔐Permissions Anyone with valid credentials can create a pre-signed URL (client side) valid credentials = Role, User, or Security Token The generated URL inherits the permissions of the credentials used to generate it This means you can generate pre-signed URLs for things you don't have access to 😅 49
  • 50. loige $ aws s3 presign s3://ireland/i-love-you https://ireland.s3.eu-west-1.amazonaws.com/i-love-you?X-Amz- Algorithm=AWS4-HMAC-SHA256&X-Amz- Credential=AKIA3ABCVQG7FGA6KKA6%2F20221115%2Feu-west- 1%2Fs3%2Faws4_request&X-Amz-Date=20221115T182036Z&X-Amz- Expires=3600&X-Amz-SignedHeaders=host&X-Amz- Signature=75749c92d94d03e411e7bbf64419f2af09301d1791b0df54c639 137c715f7888 😱 I swear I don't even know if this bucket exists or who owns it! 50
  • 51. loige Pre-signed URLs are validated at request time 51
  • 52. loige Creating a pre-signed URL programmatically AWS SDK for JavaScript v3 52
  • 53. import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' const s3Client = new S3Client() const command = new GetObjectCommand({ Bucket: "some-bucket", Key: "some-object" }) const preSignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }) console.log(preSignedUrl) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 1 2 3 const s3Client = new S3Client() 4 5 const command = new GetObjectCommand({ 6 Bucket: "some-bucket", 7 Key: "some-object" 8 }) 9 10 const preSignedUrl = await getSignedUrl(s3Client, command, { 11 expiresIn: 3600 12 }) 13 14 console.log(preSignedUrl) 15 const s3Client = new S3Client() import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' 1 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 2 3 4 5 const command = new GetObjectCommand({ 6 Bucket: "some-bucket", 7 Key: "some-object" 8 }) 9 10 const preSignedUrl = await getSignedUrl(s3Client, command, { 11 expiresIn: 3600 12 }) 13 14 console.log(preSignedUrl) 15 const command = new GetObjectCommand({ Bucket: "some-bucket", Key: "some-object" }) import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' 1 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 2 3 const s3Client = new S3Client() 4 5 6 7 8 9 10 const preSignedUrl = await getSignedUrl(s3Client, command, { 11 expiresIn: 3600 12 }) 13 14 console.log(preSignedUrl) 15 const preSignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }) import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' 1 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 2 3 const s3Client = new S3Client() 4 5 const command = new GetObjectCommand({ 6 Bucket: "some-bucket", 7 Key: "some-object" 8 }) 9 10 11 12 13 14 console.log(preSignedUrl) 15 console.log(preSignedUrl) import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' 1 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' 2 3 const s3Client = new S3Client() 4 5 const command = new GetObjectCommand({ 6 Bucket: "some-bucket", 7 Key: "some-object" 8 }) 9 10 const preSignedUrl = await getSignedUrl(s3Client, command, { 11 expiresIn: 3600 12 }) 13 14 15 import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' const s3Client = new S3Client() const command = new GetObjectCommand({ Bucket: "some-bucket", Key: "some-object" }) const preSignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }) console.log(preSignedUrl) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 loige 53
  • 55. loige 2 Options: PUT & POST 🤨 55
  • 56. loige PUT Method PUT <preSignedURL> HTTP/1.1 Host: <bucket>.s3.<region>.amazonaws.com Content-Length: 2097852 ����JFIFHH������"�� ���Dl��FW�'6N�()H�'p��FD3 [...] 56
  • 57. import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' const s3Client = new S3Client() const command = new PutObjectCommand({ Bucket: "some-bucket", Key: "some-object" }) const preSignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }) console.log(preSignedUrl) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 loige Only difference with the previous example 57
  • 58. loige PUT Method - Limitations You cannot set a limit on the upload size (max of 5 GB)! * You can limit the Content-Type but you can specify exactly one * Unless you know the exact size in advance 58
  • 59. loige POST method It uses the multipart/form-data encoding (form upload) Gives more freedom to the client to shape the request (Content-Type, file name, etc) It uses a policy mechanism to define the "rules" of what can be uploaded E.g. you can limit the supported mime types and provide a maximum file size You can use it to upload from a web form and even configure the redirect URL It's not really a URL but more of a pre-signed form! 59
  • 60. POST / HTTP/1.1 Host: <bucket>.s3.amazonaws.com Content-Type: multipart/form-data; boundary=9431149156168 Content-Length: 2097852 --9431149156168 Content-Disposition: form-data; name="key" picture.jpg --9431149156168 Content-Disposition: form-data; name="X-Amz-Credential" AKIA3SGABCDXXXA6KKA6/20221115/eu-west-1/s3/aws4_request --9431149156168 Content-Disposition: form-data; name="Policy" eyJleHBpcmF0aW9uIjoiMjAyMi0xMS0xNVQyMDo0NjozN1oiLCJjb25kaXRpb25zIjpbWyJj[...] --9431149156168 Content-Disposition: form-data; name="X-Amz-Signature" 2c1da0001dfec7caea1c9fb80c7bc8847f515a9e4483d2942464f48d2f827de7 --9431149156168 Content-Disposition: form-data; name="file"; filename="MyFilename.jpg" Content-Type: image/jpeg ����JFIFHH������"�� ���Dl��FW�'6N�()H�'p��FD3[...] --9431149156168-- loige 60
  • 61. loige POST method Policy A JSON object (Base64 encoded) that defines the upload rules (conditions) and the expiration date This is what gets signed: you cannot alter the policy without breaking the signature { "expiration": "2022-11-15T20:46:37Z", "conditions": [ ["content-length-range", 0, 5242880], ["starts-with", "$Content-Type", "image/"], {"bucket": "somebucket"}, {"X-Amz-Algorithm": "AWS4-HMAC-SHA256"}, {"X-Amz-Credential": "AKIA3SGABCDXXXA6KKA6/20221115/eu-west-1/s3/aws4_request"}, {"X-Amz-Date": "20221115T194637Z"}, {"key": "picture.jpg"} ] } 61
  • 62. import { S3Client } from '@aws-sdk/client-s3' import { createPresignedPost } from '@aws-sdk/s3-presigned-post' const { BUCKET_NAME, OBJECT_KEY } = process.env const s3Client = new S3Client() const { url, fields } = await createPresignedPost(s3Client, { Bucket: 'somebucket', Key: 'someobject', Conditions: [ ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max ], Fields: { success_action_status: '201', 'Content-Type': 'image/png' }, Expires: 3600 }) console.log({ url, fields }) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' import { S3Client } from '@aws-sdk/client-s3' 1 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 const { url, fields } = await createPresignedPost(s3Client, { }) import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 18 19 console.log({ url, fields }) 20 Bucket: 'somebucket', import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 Key: 'someobject', import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 Conditions: [ ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max ], import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 10 11 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 Fields: { success_action_status: '201', 'Content-Type': 'image/png' }, import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 13 14 15 16 Expires: 3600 17 }) 18 19 console.log({ url, fields }) 20 Expires: 3600 import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 17 }) 18 19 console.log({ url, fields }) 20 console.log({ url, fields }) import { S3Client } from '@aws-sdk/client-s3' 1 import { createPresignedPost } from '@aws-sdk/s3-presigned-post' 2 3 const { BUCKET_NAME, OBJECT_KEY } = process.env 4 const s3Client = new S3Client() 5 6 const { url, fields } = await createPresignedPost(s3Client, { 7 Bucket: 'somebucket', 8 Key: 'someobject', 9 Conditions: [ 10 ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max 11 ], 12 Fields: { 13 success_action_status: '201', 14 'Content-Type': 'image/png' 15 }, 16 Expires: 3600 17 }) 18 19 20 import { S3Client } from '@aws-sdk/client-s3' import { createPresignedPost } from '@aws-sdk/s3-presigned-post' const { BUCKET_NAME, OBJECT_KEY } = process.env const s3Client = new S3Client() const { url, fields } = await createPresignedPost(s3Client, { Bucket: 'somebucket', Key: 'someobject', Conditions: [ ['content-length-range', 0, 5 * 1024 * 1024] // 5 MB max ], Fields: { success_action_status: '201', 'Content-Type': 'image/png' }, Expires: 3600 }) console.log({ url, fields }) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 loige 62
  • 63. // you can use `url` and `fields` to generate an HTML form const code = `<h1>Upload an image to S3</h1> <form action="${url}" method="post" enctype="multipart/form-data"> ${Object.entries(fields).map(([key, value]) => { return `<input type="hidden" name="${key}" value="${value.replace(/"/g, '&quot;')}">` }).join('n')} <div><input type="file" name="file" accept="image/png"></div> <div><input type="submit" value="Upload"></div> </form>` 1 2 3 4 5 6 7 8 9 10 loige 63
  • 64. loige Limitations and quirks It supports only 1 file (cannot upload multiple files in one go) The file field must be the last entry in the form (S3 will ignore every other field after the file) From the browser (AJAX) you need to enable CORS on the bucket 64
  • 65. loige Should I use PUT or POST? 🧐 PUT is simpler but definitely more limited POST is slightly more complicated (and less adopted) but it's more flexible You should probably put some time into learning POST and use that! 65
  • 66. loige Pre-signed URLs for other operations S3 pre-signed URLs are not limited to GET, PUT or POST operations You can literally create pre-signed URLs for any command (DeleteObject, ListBuckets, MultiPartUpload, etc...) 66
  • 67. loige Do you need moar examples? 😼 github.com/lmammino/s3-presigned-urls-examples 67
  • 68. loige ... In summary S3 pre-signed URLs are a great way to authorise operations on S3 They are generally used to implement upload/download features The signature is created client-side so you can sign anything. Access is validated at request time This is not the only solution, you can also use the JavaScript SDK from the frontend and get limited credentials from Cognito (Amplify makes that process simpler) For upload you can use PUT and POST, but POST is much more flexible 💬PS: Meower.com doesn't really exist... but... do you want to invest?! It's a great idea, trust me! 68
  • 69. Cover photo by on Kelly Sikkema Unsplash fourtheorem.com THANKS! 🙌 fth.link/presign loige It's a wrap! 69