82-93


(82) Section intro

  1. introduce AWS Amplify in the browser
  2. Get temporary credentials in the Browser
  3. CORS issues
  4. 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?

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