AWS & Typescript Masterclass - 10. Using AWS inside a React project with Amplify
September 17th, 2022
82-93
(82) Section intro
introduce AWS Amplify in the browser
Get temporary credentials in the Browser
CORS issues
App will be approaching it's final form
(83) Setup and Amplify install
npm i aws-amplify @aws-amplify/auth
npm i aws-sdk
(84) Cognito login from React code
model.ts
import {CognitoUser} from '@aws-aplify/auth'
export interface User {
userName: string
cognitoUser: CognitoUser
}
service/AuthService
import {Auth} from "aws-amplify";
import Amplify from "aws-amplify";
import {CognitoUser} from "@aws-amplify/auth";
import {config} from "./config";
import * as AWS from "aws-sdk";
import { Credentials } from 'aws-sdk/lib/credentials'
Amplify.configure({
Auth: {
mandatorySignIn: false,
region: config.REGION,
userPoolId: config.USER_POOL_ID,
userPoolWebClientId: config.APP_CLIENT_ID,
identityPoolId: config.IDENTITY_POOL_ID,
authenticationFlowType: 'USER_PASSWORD_AUTH'
}
})
export class AuthService {
public async login(userName: string, password: string): Promise<User|undefined>{
try {
const user = await Auth.signIn(userName, password) as CognitoUser;
return {
cognitoUser: user,
userName: user.getUsername(),
}
catch (error) {
return undefined
}
}
public async getUserAttributes(user: User):Promise<UserAttribute[]>{
const result: UserAttribute[] = [];
const attributes = await Auth.userAttributes(user)
result.push(...attributes)
return result
}
}
(86) Photo bucket name and bucket CORS
buckets need globally unique name
use stack-id
const shortStackId = Fn.select(2, Fn.split('/', this.stackId))
suffix = Fn.select(4, Fn.split('-', shortStackId))
this.spacesPhotosBucket = new Bucket(this, 'spaces-photos', {
bucketName: 'spaces-photos-' + this.suffix,
cors: [{
allowedMethods: [
HttpMethods.HEAD,
HttpMethods.GET,
HttpMethods.PUT,
],
allowedOrigins: ['*'],
allowedHeaders: ['*'],
}]
})
new CfnOutput(this, 'spaces-photos-bucket-name', {
value: this.spacesPhotosBucket.bucketName
})
(87) Passing the bucket ARN to Auth
this.adminRole.addToPolicy(new PolicyStatement({
effect: Effect.ALLOW,
actions: [
's3:PutObject',
's3:PutObjectAcl', // publicly accessible objects
],
resources: [this.photoBucketArn]
}))
(88) Lambda CORS
ref: CORS in 100 Seconds - YouTube
result.headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
}
(90) AWS credentials in the browser
public async getAWSTemporaryCreds(user: CognitoUser){
const cognitoIdentityPool = `cognito-idp.${config.REGION}.amazonaws.com/${config.USER_POOL_ID}`;
const creds = new AWS.CognitoIdentityCredentials({
IdentityPoolId: config.IDENTITY_POOL_ID,
Logins: {
[cognitoIdentityPool]: user.getSignInUserSession()!.getIdToken().getJwtToken()
}
}, {
region: config.REGION
});
AWS.config.credentials = creds;
await this.refreshCredentials();
return creds
}
private async refreshCredentials(): Promise<void>{
return new Promise((resolve, reject)=>{
(AWS.config.credentials as Credentials).refresh(err =>{
if (err) {
reject(err)
} else {
resolve()
}
})
})
}
?? do we really need to set it globally on AWS.config.credentials?
probably yes, if we want calls to work
otherwise we'll probably need to pass credentials around everywhere in our UI components
?? do we really need to refresh the credentials for it to work?
(91) Uploading public files
import { S3, config as S3Config}
S3Config.update({region: appConfig.REGION})
const s3Client = new S3({region: appConfig.REGION})
const uploadResult = await new S3({region: appConfig.REGION}).upload({
Bucket: appConfig.SPACES_PHOTOS_BUCKET,
Key: generateRandomFilename(),
Body: file,
ACL: 'public-read'
}).promise
return uploadResult.Location
> upload Key should be random
the creation of the lib needs to happen lazy
if it is created before AWS credentials are set globally, it will never get the credentials
new S3({region: appConfig.REGION})
"Maybe I'll do this with a lazy singleton later"
?? what is a lazy singleton - react feature? typescript feature? or custom-built Singleton pattern?
-> custom-built Singleton pattern
private getS3Client():S3 {
if(!this.s3Client) {
this.client = new S3({region: appConfig.REGION})
}
return this.s3Client
}
(92) Creating spaces
const requestOptions: RequestInit = {
method: 'POST',
body: JSON.stringify(data),
}
const result = await fetch(requestUrl, requestOptions)
const json = await result.json()
(93) Getting spaces
const result = await fetch(requestUrl, {method: 'GET'})
const json = await result.json()
This post was referenced in: