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?
- TypeScript: Pattern: Lazy Singleton
- -> 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()