I have a ReactJs application that retrieves an S3 URL from the API and get a pre-signed version of it using the Amplify Storage
library.
This URL is the source of a video, the code is something like this:
Storage.get(
outputVideoSrc,
{ expires: 43200 }
));
But I have noticed that even tho the expiration time is really long, after ~1 hour the video stops playing, showing a message of Network error and aborting the playback.
If I get the video URL and try to access it from my browser I see an error similar to it:
<Error>
<Code>ExpiredToken</Code>
<Message>The provided token has expired.</Message>
<Token-0>
...
After some googling, I found that this expiration is due to the authentication token being expired, not the pre-signed URL per se. However, I need this URL to work for more than 1 hour, so the user can work with the video for long period of time.
It seems that it is not possible to customize how long the token will last on Amplify: https://github.com/aws-amplify/amplify-js/issues/2714
I have also tried to keep reloading the video after some time in the hope that I get a refreshed token, but:
Which alternatives do I have in order to make this work and not having the video stop playing all the time?
The only way I have found to fix it is to use a lambda function to pre-sign the URL and use it to resolve a GraphQL field on my schema.
So, on using the amplify-cli
I have added a new function
amplify function add
In the example here I'm naming it getUrls
My function has a code similar to this:
import boto3
from botocore.exceptions import ClientError
s3_client = boto3.client('s3')
bucket = 'my-bucket'
def handler(event, context):
print('received event:')
print(event)
urls = []
if 'typeName' in event and event['typeName'] == 'MyType' and event['fieldName'] == 'urls':
print('Requesting field keys for MyType...', event['source']['keys'])
for key in event['source']['keys']:
try:
signed_url = s3_client.generate_presigned_url(
'get_object',
Params={
'Bucket': bucket,
'Key': key,
},
ExpiresIn=86400 # 24 hours
)
print('Signed URL: ', signed_url)
if signed_url:
urls.append(signed_url)
except ClientError as e:
print('Error', e)
return urls
Don't forget to add permission to S3:GetObject
on the bucket for your function
on getUrls-cloudoformation-template.json
Add on your PolicyDocument
:
{
"Sid": "S3",
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::mybucket/*"
}
Then add it as a resolver for your field on the schema.graphql
type MyType @model
{
id: ID!
keys: [String!]
urls [String!] @function(name: "getUrls-${env}")
}
That is it, when you request for the field urls
it will return the pre-signed url valid for 24 hours, you can then change it to any duration you want.