Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

SignedURLS

shamikatamazon edited this page Apr 30, 2021 · 9 revisions

SignedURLs

We now support the AWS best practice concerning the creation of signed URLs: Choosing between trusted key groups (recommended) and AWS accounts

Why using signed URLs ?

Signed URLs are used to protect the access on ressources (on an S3 bucket for example). Using the best practice enable us to sign URLs without having to create a private/public key pair with a root AWS account.

Behind the scene

When choosing the production environment option when setting up VOD, the CLI will create a private/public key pair (.pem file). The Private key will be stored with AWS Secrets Manager and the Public key will be added to the CloudFormation template.

How URLs are signed ?

When creating a signed URL, we create a Policy statement that controls the access that a signed URL grants to a user; the Signature, a hashed, signed, and base64-encoded version of the JSON policy statement that uses the create Policy and the Key-Pair-ID, this public key must belong to a key group that is a trusted signer in the distribution

How to create a signed URL

As a part of the Amplify video deploy, a Lambda which can generate the signature for an URL is deployed and exposed via the GraphQL API as a part of the videoObject. The videoObject generating a token can be verified by reviewing the schema.graphql which contains the call to generate a token

#DO NOT EDIT
type videoObject @model
@auth(
  rules: [
    {allow: owner, ownerField: "owner", operations: [create, update, delete, read] },
    {allow: groups, groups:["Admin"], operations: [create, update, delete, read]},
    {allow: private, operations: [read]}
  ]
)
{
  id:ID!

  #indicates that a token will be returned as a part of the video object
  token: String @function(name: "eighttrial-prod-tokenGen")
}

Double check the graphql/queries.js to ensure that the token is being returned as a part of the call

export const getVodAsset = /* GraphQL */ `
  query GetVodAsset($id: ID!) {
    getVodAsset(id: $id) {
      id
      title
      description
      video {
        id
        createdAt
        updatedAt
        owner
        token /* <<<< indicates a token will be returned in the response >>>>*/
      }
      createdAt
      updatedAt
      owner
    }
  }
`;
export const listVodAssets = /* GraphQL */ `
  query ListVodAssets(
    $filter: ModelvodAssetFilterInput
    $limit: Int
    $nextToken: String
  ) {
    listVodAssets(filter: $filter, limit: $limit, nextToken: $nextToken) {
      items {
        id
        title
        description
        video {
          id
          createdAt
          updatedAt
          owner
          token /* <<<< indicates a token will be returned in the response >>>>*/
        }
        createdAt
        updatedAt
        owner
      }
      nextToken
    }
  }
`;

The generated token needs to be appended to the CDN URL of the video. For example https://d3sdhpxwi5ukf4.cloudfront.net/cd2b9016-f14b-448b-b602-a0372f429e06/cd2b9016-f14b-448b-b602-a0372f429e06.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9kM3NkaHB4d2k1dWtmNC5jbG91ZGZyb250Lm5ldC9jZDJiOTAxNi1mMTRiLTQ0OGItYjYwMi1hMDM3MmY0MjllMDYvKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTYxOTczMTM5Nn19fV19&Key-Pair-Id=K1H5X7ZK6IID8W&Signature=QQ8j0-HUfvniarWzHISAad0iK0ShA7U3y5aB~yqMBr5PCHi17iJ4I2FtkFHTBpGGAxZEAIccNzoBtwP5Jrwk0yRkDS12j4z2kHdWjg8OsQm2~uL7rSUynTFpBJRD1Xv1jlLRATp-FK4tvktpTDv7Z2LdrFFRqJpmBqao-AS1nYO5FzwizmdtxqQwfJ1~md8P9VwUCqfnLBUgfMRNYzGTfrJn5yEqYW9AsVrkssijiepla1WH4oyCcJ-v62CmQ4QLM11iQ7vFX5omNPLajnIP3PDek1zn9CiNPCxiqRXcTkS1NjprqEk49syDL6HZZfldZTNO4HHofrQhvdo20CGdfA__

where https://d3sdhpxwi5ukf4.cloudfront.net/cd2b9016-f14b-448b-b602-a0372f429e06/cd2b9016-f14b-448b-b602-a0372f429e06.m3u8 is the CDN URL of the video and ?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9kM3NkaHB4d2k1dWtmNC5jbG91ZGZyb250Lm5ldC9jZDJiOTAxNi1mMTRiLTQ0OGItYjYwMi1hMDM3MmY0MjllMDYvKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTYxOTczMTM5Nn19fV19&Key-Pair-Id=K1H5X7ZK6IID8W&Signature=QQ8j0-HUfvniarWzHISAad0iK0ShA7U3y5aB~yqMBr5PCHi17iJ4I2FtkFHTBpGGAxZEAIccNzoBtwP5Jrwk0yRkDS12j4z2kHdWjg8OsQm2~uL7rSUynTFpBJRD1Xv1jlLRATp-FK4tvktpTDv7Z2LdrFFRqJpmBqao-AS1nYO5FzwizmdtxqQwfJ1~md8P9VwUCqfnLBUgfMRNYzGTfrJn5yEqYW9AsVrkssijiepla1WH4oyCcJ-v62CmQ4QLM11iQ7vFX5omNPLajnIP3PDek1zn9CiNPCxiqRXcTkS1NjprqEk49syDL6HZZfldZTNO4HHofrQhvdo20CGdfA__ is the signature that is returned as a part of the GraphQL call

Example code

import React from 'react';
import Amplify, { API, graphqlOperation, Auth } from 'aws-amplify';
import {listVodAssets, getVodAsset} from '../../graphql/queries.js'
import awsmobile from '../../aws-exports';
import { withAuthenticator } from 'aws-amplify-react';

class ListView extends React.Component {

	constructor(){
		super()
		this.state={ token : '', videoId: ''};
		this.videoAssetId = "b5122cc3-4b46-479e-a98b-78f312ef2664"
	}

	componentDidMount(){

		const videoObject = {
     	        id: this.videoAssetId
     	};

		API.graphql(graphqlOperation(getVodAsset, videoObject)).then((response, error) => {
					
					console.log(response["data"]["getVodAsset"])
					const token = response["data"]["getVodAsset"]["video"]["token"]
					const videoId = response["data"]["getVodAsset"]["video"]["id"]
					console.log(token)
					this.setState({
						token, videoId
					})
		});



	}

	render(){

		//cd2b9016-f14b-448b-b602-a0372f429e06
		//vod asset id - b5122cc3-4b46-479e-a98b-78f312ef2664
		return <p>https://d3sdhpxwi5ukf4.cloudfront.net/{this.state.videoId}/{this.state.videoId}.m3u8{ this.state.token } </p>

	}

}


export default withAuthenticator(ListView, true);

Sources