Kiến thức

Serverless Series (Golang) – Bài 5 – Authentication with Amazon Cognito – Part 1 12 tháng 04, 2022 – 202 lượt xem Golang DevOps AWS Đầu mục bài viết Bài viết liên quan Khoá học hay

Giới thiệu

Chào mừng các bạn tới với loạt bài Serverless, ở bài trước chúng ta đã nói về cách sử dụng S3 để lưu trữ Phần mềm Trang Đơn và lưu trữ hình ảnh, cách sử dụng Lambda để tải tệp lên trên S3. Và vì tất cả các url của chúng tôi đều ở cơ chế công khai nên bất cứ người nào cũng có thể truy cập trang tạo để tạo dữ liệu và tải tệp lên S3, điều nhưng mà chúng tôi ko muốn. Thành ra, trong bài viết này chúng ta sẽ mày mò về thành phần tiếp theo được sử dụng để tiến hành chính xác và điều hành người mua, đấy là AWS Cognito.

Hệ thống nhưng mà chúng tôi sẽ xây dựng như sau.

Trong bài trước, chúng ta đã xây dựng phần API Gateway + Lambda + DynamoDB + S3.

Trong bài viết này, chúng tôi sẽ thêm AWS Cognito.

AWS Cognito

AWS Cognito là 1 dịch vụ AWS được sử dụng để chính xác người mua. Sử dụng nó sẽ giúp chúng tôi xây dựng thứ tự đăng nhập, đăng ký, xác minh email, chỉnh sửa mật khẩu, mật khẩu khởi động lại, v.v. đơn giản hơn, thay vì phải xây dựng DB cho người mua và tiến hành thủ công nhiều việc như JWT, mật khẩu băm. , gửi thư xác minh, v.v.

Tác dụng bình thường của AWS Cognito dành cho các hệ thống SSO (đăng nhập 1 lần). Thí dụ, hệ thống của chúng ta có 3 dịch vụ không giống nhau, mỗi dịch vụ đều có trang đăng nhập và người mua DB riêng, lúc hệ thống của chúng ta tăng trưởng lên tới hàng chục dịch vụ thì việc xây dựng mỗi hệ thống sử dụng chính xác riêng sẽ rất khó điều hành người mua. Biện pháp là chúng tôi sẽ xây dựng 1 hệ thống SSO, chúng tôi chỉ điều hành người mua tại 1 nơi, sau lúc đăng nhập vào hệ thống SSO này, token của nó sẽ được sử dụng cho nhiều dịch vụ không giống nhau phía sau.

Hình ảnh từhereinc.com

AWS Cognito sẽ có 2 thành phần chính: nhóm người mua với nhóm nhận dạng.

  • User pool giống như 1 DB chứa người mua của chúng ta, chúng ta sẽ tiến hành đăng ký, đăng nhập với user pool này và nó sẽ trả cho chúng ta 1 mã công bố, trong bài viết này chúng ta sẽ sử dụng user pool.
  • Nhóm tính danh gần giống như nhóm người mua ở chỗ nó được sử dụng để giữ người mua, mà có 1 sự dị biệt là mã công bố được trả về từ nhóm nhận dạng có thể được sử dụng để truy cập các dịch vụ AWS khác.

1 số cách để khai triển ẩn danh

Giao diện người mua + AWS Cognito

Bằng cách này, FE sẽ tương tác trực tiếp với AWS Cognito bằng cách sử dụng khung Aws Amplify.

Lợi thế:

  • Chỉ cần tạo AWS Cognito trên AWS, DevOps ko cần bất cứ thao tác nào.
  • AWS sẽ điều hành việc chia tỉ lệ theo khối lượng công tác của người mua.
  • Chỉ cần mã FE.

Thiếu sót:

  • AWS Cognito phải được tạo ở cơ chế công khai.
  • Hoặc tạo với cơ chế riêng tây, khóa bí hiểm để kết nối với AWS Cognito phải được lưu trữ dưới mã nguồn FE.
  • Bảo mật kém.

Front-end + Cổng API + AWS Lambda + AWS Cognito

Với cách này, chúng ta sẽ sử dụng lambda để tương tác với AWS Cognito, nếu có khóa bí hiểm, chúng ta sẽ lưu nó trên AWS và chuyển cho Lambda dưới dạng biến env để bảo đảm tính bảo mật.

Lợi thế:

  • An toàn hơn so với mẫu hình FE + AWS Cognito.
  • Thao tác ít hơn, chỉ cần điều hành 1 tác dụng là đăng nhập.

Thiếu sót:

  • AWS Lambda ở Khu vực Singapore chỉ có thể thực thi tối đa 500 cùng lúc mỗi phút.
  • Chẳng thể tự mình kiểm soát vấn đề quy mô.

Có nhiều cách khác, mà tôi sẽ ko nói về nó ở đây. Trong bài học này, vì chúng ta đang mày mò về Lambda nên chúng ta sẽ sử dụng bí quyết Front-end + Cổng API + AWS Lambda + AWS Cognito 😂.

Tạo nhóm người mua

Đi đến Bảng điều khiển AWS, kiếm tìm Cognito, nhấp vào Cognito và nhấp vào tạo nhóm người mua. Chúng ta sẽ thấy giao diện người mua như sau.

image.png

Chỉ cần rà soát nhóm người mua ẩn danh. Cuộn xuống phần Tùy chọn đăng nhập nhóm người mua ẩn danh, rà soát email, nhấp vào Tiếp theo.

image.png

Qua bước 2, trong phần Chuẩn xác đa nhân tố, chọn Ko có MFA. Các mục còn lại được để mặc định.

image.png

Bước 3, bỏ chọn Bật tự đăng ký, để mặc định phần còn lại, nhấn Tiếp theo.

image.png

Bước 4, trong phần Email, bạn chọn Gửi thư bằng Ẩn danh.

image.png

Bước 5, ở phần User pool name và App client, bạn điền gì cũng được, mình điền tên cognito-serverless-series.

image.png

Trong trường phần mềm lúc đầu, hãy nhập cognito-serverless-series.

image.png

Trong phần Thứ tự chính xác, hãy nhớ chọn mục ALLOW_USER_PASSWORD_AUTH.

image.png

Sau lúc điền xong, bạn bấm Tiếp theo để sang bước 6. Xem lại và bấm Tạo nhóm người mua.

image.png

Ok, sau lúc chúng ta đã tạo xong user pool, bước tiếp theo chúng ta sẽ xây dựng lại hệ thống ở bài trước và viết mã cho tác dụng đăng nhập Lambda, thêm vào đường dẫn đăng nhập API Gateway, cập nhật đường dẫn tạo sách để đề nghị chính xác.

Cấp phép hệ thống trước đấy

Mình sẽ dùng terraform để tạo lại hệ thống, bạn nào muốn biết cách tạo thủ công từng bước thì xem từ bài 2. Các bạn tải mã nguồn tại git repo này https://github.com/hoalongnatsu/serverless -series.git.

Chuyển di tới folder bai-5 / terraform-start. Trong cơ chế tệp / lambda_policy.json, dòng "Resource": "arn:aws:dynamodb:us-west-2:<ACCOUNT_ID>:table/books"cập nhật 1 lần nữa với id nick của bạn. Xong rồi chạy lệnh.

terraform init
terraform apply -auto-approve

Sau lúc Terraform chạy xong, nó sẽ in ra URL cổng API đầu cuối và URL trang web.

base_url = {
"api" = "https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging"
"web" = "http://serverless-series-spa.s3-website-us-west-2.amazonaws.com"
}

Sau lúc Terraform chạy xong, chúng ta sẽ có các khoáng sản sau.

image.png

Lambda

image.png

Cổng API

image.png

DynamoDB

image.png

Thùng S3

Tiếp theo, chúng ta sao chép đường dẫn api trong terminal ở trên, chuyển tới folder bai-5 / front-end, trong tệp .env-cmdrccập nhật lại trường staging.REACT_APP_API_URL: https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging. Sau đấy chạy lệnh.

yarn install
yarn build:staging

Nó sẽ xây dựng mã FE và tạo 1 folder xây dựng, chúng tôi sẽ tải folder xây dựng lên thùng máy chủ S3 serverless-series-spa.

aws s3 cp build s3://serverless-series-spa/ --recursive

Ok, hiện thời chúng ta truy cập vào http://serverless-series-spa.s3-website-us-west-2.amazonaws.com, chúng ta sẽ thấy trang SPA của mình ở bài trước. Bạn nên đọc các bài viết trước để thông suốt từng nguồn tài liệu trên.

Tích hợp ẩn danh

Tạo người mua

Trước nhất để tương tác với Cognito, chúng ta sẽ tạo 1 người phục vụ nhóm người mua Cognito, chúng ta có thể viết Lambda để tiến hành việc tạo người mua này, ở đây chúng ta sẽ sử dụng AWS Console Web để tạo người mua 1 cách mau chóng. Đi đến Ẩn danh, trong phần Người mua, nhấp vào Tạo người mua.

image.png

Chúng ta sẽ thấy giao diện người mua như sau.

Chọn như trong hình, điền email và mật khẩu của người mua, sau đấy bấm tạo.

Chúng ta sẽ thấy 1 văn bản màu xanh lam Buộc chỉnh sửa mật khẩu, người mua được tạo trong Cognito đề nghị lúc chúng tôi đăng nhập lần trước tiên, chúng tôi phải chỉnh sửa mật khẩu của nó, thành ra chúng tôi có tác dụng chỉnh sửa mật khẩu. Ok, sau lúc tạo xong user, hiện thời chúng ta sẽ thực hiện viết code.

Khai triển chỉnh sửa mật khẩu Lambda

Chúng ta viết mã cho tác dụng chỉnh sửa mật khẩu, chuyển tới folder bai-5 / code.

├── change-password
│   ├── build.sh
│   ├── go.mod
│   ├── go.sum
│   └── main.go
└── login
    ├── build.sh
    ├── go.mod
    ├── go.sum
    └── main.go

Cập nhật tệp change-password / main.go như sau.

package main

import (
	"context"
	"encoding/json"
	"net/http"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
)

type Body struct {
	Username    string `json:"username"`
	OldPassword string `json:"old_password"`
	NewPassword string `json:"new_password"`
}

func ChangePassword(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	var body Body
	b64String, _ := base64.StdEncoding.DecodeString(req.Body)
	rawIn := json.RawMessage(b64String)
	bodyBytes, err := rawIn.MarshalJSON()
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusBadRequest,
			Body:       err.Error(),
		}, nil
	}

	json.Unmarshal(bodyBytes, &body)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusBadRequest,
			Body:       err.Error(),
		}, nil
	}

	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusInternalServerError,
			Body:       "Error while retrieving AWS credentials",
		}, nil
	}

	cip := cognitoidentityprovider.NewFromConfig(cfg)
	authInput := &cognitoidentityprovider.InitiateAuthInput{
		AuthFlow: "USER_PASSWORD_AUTH",
		ClientId: aws.String("6jk1bh3me5h1onmbjhqalmtpp8"), // Should os.Getenv("CLIENT_ID")
		AuthParameters: map[string]string{
			"USERNAME": body.Username,
			"PASSWORD": body.OldPassword,
		},
	}
	authResp, err := cip.InitiateAuth(context.TODO(), authInput)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusInternalServerError,
			Body:       err.Error(),
		}, nil
	}

	challengeInput := &cognitoidentityprovider.RespondToAuthChallengeInput{
		ChallengeName: "NEW_PASSWORD_REQUIRED",
		ClientId:      aws.String("6jk1bh3me5h1onmbjhqalmtpp8"),
		ChallengeResponses: map[string]string{
			"USERNAME":     body.Username,
			"NEW_PASSWORD": body.NewPassword,
		},
		Session: authResp.Session,
	}
	challengeResp, err := cip.RespondToAuthChallenge(context.TODO(), challengeInput)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusInternalServerError,
			Body:       err.Error(),
		}, nil
	}

	res, _ := json.Marshal(challengeResp)
	return events.APIGatewayProxyResponse{
		StatusCode: http.StatusOK,
		Headers: map[string]string{
			"Content-Type":                "application/json",
			"Access-Control-Allow-Origin": "*",
		},
		Body: string(res),
	}, nil
}

func main() {
	lambda.Start(ChangePassword)
}

Trước nhất, vì trong bài trước, chúng tôi đã thêm trị giá Loại dụng cụ nhì phân */*.

image.png

Thành ra, đề nghị sẽ được mã hóa và chúng tôi chẳng thể lấy req.body using function json.Unmarshal([]byte(req.Body), &body) Được, mà chúng ta phải làm như sau.

var body Body
b64String, _ := base64.StdEncoding.DecodeString(req.Body)
rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()
if err != nil {
    return events.APIGatewayProxyResponse{
        StatusCode: http.StatusBadRequest,
        Body:       err.Error(),
    }, nil
}

json.Unmarshal(bodyBytes, &body)
if err != nil {
    return events.APIGatewayProxyResponse{
        StatusCode: http.StatusBadRequest,
        Body:       err.Error(),
    }, nil
}

Để tương tác với Cognito, trước nhất chúng tôi sẽ tạo 1 phần mềm khách cognito bằng cách sử dụng hàm cip := cognitoidentityprovider.NewFromConfig(cfg). Sau đấy, chúng tôi thực hiện lấy thông tin chính xác của người mua bằng mã.

authInput := &cognitoidentityprovider.InitiateAuthInput{
    AuthFlow: "USER_PASSWORD_AUTH",
    ClientId: aws.String("6jk1bh3me5h1onmbjhqalmtpp8"), // Should os.Getenv("CLIENT_ID")
    AuthParameters: map[string]string{
        "USERNAME": body.Username,
        "PASSWORD": body.OldPassword,
    },
}
authResp, err := cip.InitiateAuth(context.TODO(), authInput)
if err != nil {
    return events.APIGatewayProxyResponse{
        StatusCode: http.StatusInternalServerError,
        Body:       err.Error(),
    }, nil
}

Sau lúc lấy được thông tin của người mua, chúng tôi sẽ thực hiện đổi mật khẩu bằng mã.

challengeInput := &cognitoidentityprovider.RespondToAuthChallengeInput{
    ChallengeName: "NEW_PASSWORD_REQUIRED",
    ClientId:      aws.String("6jk1bh3me5h1onmbjhqalmtpp8"),
    ChallengeResponses: map[string]string{
        "USERNAME":     body.Username,
        "NEW_PASSWORD": body.NewPassword,
    },
    Session: authResp.Session,
}
challengeResp, err := cip.RespondToAuthChallenge(context.TODO(), challengeInput)
if err != nil {
    return events.APIGatewayProxyResponse{
        StatusCode: http.StatusInternalServerError,
        Body:       err.Error(),
    }, nil
}

Sau đấy, chúng tôi trả lại kết quả chính xác của người phục vụ máy khách. Trị giá ClientId, chúng tôi thu được từ tích hợp Phần mềm trong Cognito.

image.png

Trong thực tiễn, bạn nên sử dụng os.Getenv("CLIENT_ID") thay vì điền trực tiếp vào hàm. Sau lúc viết mã, chúng tôi khởi đầu gói và cập nhật tác dụng AWS Lambda.

go get
sh build.sh
aws lambda update-function-code --function-name change_password --zip-file fileb://change-password.zip --region us-west-2

Hiện giờ chúng tôi sẽ rà soát xem tác dụng chỉnh sửa mật khẩu của chúng tôi có hoạt động chuẩn xác hay ko.

curl -sX POST -d '{"username":"hoalongnatsu@gmail.com", "old_password": "12345678@aA", "new_password": "87654321@bB"}' https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging/change-password

Kết quả trả về.

{
    "AuthenticationResult": {
        "AccessToken": "eyJ...",
        "ExpiresIn": 3600,
        "IdToken": "eyJ...",
        "NewDeviceMetadata": null,
        "RefreshToken": "eyJ...",
        "TokenType": "Bearer"
    },
    "ChallengeName": "",
    "ChallengeParameters": {},
    "Session": null,
    "ResultMetadata": {}
}

Nếu bạn trông thấy kết quả trên thì có tức là hàm của chúng ta đang hoạt động chuẩn xác

Khai triển đăng nhập Lambda

Tiếp theo chúng ta sẽ viết mã cho tác dụng đăng nhập, cập nhật file login / main.go như sau.

package main

import (
	"context"
	"encoding/json"
	"net/http"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
)

type Body struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

func login(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	var body Body
	b64String, _ := base64.StdEncoding.DecodeString(req.Body)
	rawIn := json.RawMessage(b64String)
	bodyBytes, err := rawIn.MarshalJSON()
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusBadRequest,
			Body:       err.Error(),
		}, nil
	}

	json.Unmarshal(bodyBytes, &body)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusBadRequest,
			Body:       err.Error(),
		}, nil
	}

	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusInternalServerError,
			Body:       "Error while retrieving AWS credentials",
		}, nil
	}

	cip := cognitoidentityprovider.NewFromConfig(cfg)
	authInput := &cognitoidentityprovider.InitiateAuthInput{
		AuthFlow: "USER_PASSWORD_AUTH",
		ClientId: aws.String("6jk1bh3me5h1onmbjhqalmtpp8"), // Should os.Getenv("CLIENT_ID")
		AuthParameters: map[string]string{
			"USERNAME": body.Username,
			"PASSWORD": body.Password,
		},
	}
	authResp, err := cip.InitiateAuth(context.TODO(), authInput)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusInternalServerError,
			Body:       err.Error(),
		}, nil
	}

	res, _ := json.Marshal(authResp)
	return events.APIGatewayProxyResponse{
		StatusCode: http.StatusOK,
		Headers: map[string]string{
			"Content-Type":                "application/json",
			"Access-Control-Allow-Origin": "*",
		},
		Body: string(res),
	}, nil
}

func main() {
	lambda.Start(login)
}

Tính năng đăng nhập gần giống như tác dụng chỉnh sửa mật khẩu. Sau lúc cập nhật mã, chúng tôi khởi đầu gói và cập nhật lại tác dụng AWS Lambda.

go get
sh build.sh
aws lambda update-function-code --function-name login --zip-file fileb://login.zip --region us-west-2

Hãy rà soát tác dụng đăng nhập của chúng tôi.

curl -sX POST -d '{"username":"hoalongnatsu@gmail.com", "password": "87654321@bB"}' https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging/login

Kết quả.

{
    "AuthenticationResult": {
        "AccessToken": "eyJ...",
        "ExpiresIn": 3600,
        "IdToken": "eyJ...",
        "NewDeviceMetadata": null,
        "RefreshToken": "eyJ...",
        "TokenType": "Bearer"
    },
    "ChallengeName": "",
    "ChallengeParameters": {},
    "Session": null,
    "ResultMetadata": {}
}

Ok, tác dụng của tôi đã hoạt động 😁. Trị giá trong trường AuthenticationResult sẽ được sử dụng lúc chúng ta cần gọi 1 API có chính xác. Trong phần này, chúng ta chỉ tới phần đăng nhập và chỉnh sửa mật khẩu.

Kết luận

Thành ra, chúng ta đã mày mò Cognito là gì và làm thế nào để tích hợp Lambda với nó. Sử dụng Cognito sẽ ô sin điều hành và chính xác người mua trở thành dễ dãi hơn nhiều. Nếu có thắc mắc hoặc cần trả lời rõ hơn, bạn có thể hỏi trong phần bình luận bên dưới. Trong phần 2, tôi sẽ tiếp diễn nói về cách tích hợp API Gateway với Cognito để nó tự động chính xác người phục vụ chúng ta.

Hứa gặp lại tất cả các bạn trong phần 2 của bài viết này,


Thông tin thêm

Serverless Series (Golang) – Bài 5 – Authentication with Amazon Cognito – Part 1
12 tháng 04, 2022 – 202 lượt xem
Golang DevOps AWS
Đầu mục bài viết
Bài viết liên can
Khoá học hay

#Serverless #Series #Golang #Bài #Authentication #Amazon #Cognito #Part #tháng #lượt #xem #Golang #DevOps #AWS #Đầu #mục #bài #viếtBài #viết #liên #quanKhoá #học #hay
[rule_3_plain] #Serverless #Series #Golang #Bài #Authentication #Amazon #Cognito #Part #tháng #lượt #xem #Golang #DevOps #AWS #Đầu #mục #bài #viếtBài #viết #liên #quanKhoá #học #hay
Giới thiệuChào các bạn đến với series về Serverless, ở bài trước chúng ta đã nói về cách sử dụng S3 để hosting 1 trang Single Page Application và lưu trữ hình ảnh, cách sử dụng Lambda để upload file lên trên S3. Và do tất cả url của ta đều là public nên bất kỳ người nào cũng có thể vào trang create để tạo dữ liệu và upload file lên trên S3 cả, nhưng mà ta sẽ ko muốn tương tự. Nên ở bài này chúng ta sẽ mày mò về 1 thành phần tiếp theo được sử dụng để tiến hành việc authentication và điều hành user, là AWS Cognito.Hệ thống nhưng mà ta sẽ xây dựng như sau.Ở bài trước, ta đã dựng được phần API Gateway + Lambda + DynamoDB + S3.Ở bài này ta sẽ thêm vào AWS Cognito.AWS CognitoAWS Cognito là 1 dịch vụ của AWS nhưng mà được sử dụng cho việc chứng nhận (authentication) user. Sử dụng nó sẽ giúp ta trong việc xây dựng luồng sign-in, sign-up, verify email, change password, restart password, v…v… 1 cách đơn giản hơn, thay vì ta phải tự xây dựng DB cho user và tự làm nhiều thứ như JWT, hash password, send mail verify, v…v…1 usecase bình thường của AWS Cognito là sử dụng cho hệ thống SSO (single sign-on). Thí dụ là hệ thống của ta có 3 dịch vụ không giống nhau, mỗi dịch vụ đều có 1 trang đăng nhập và DB user riêng, lúc hệ thống ta tăng trưởng lên đến mấy chục dịch vụ, việc xây dựng mỗi hệ thống đều sử dụng thằng authentication của riêng nó sẽ khiến ta rất khó để điều hành user. Biện pháp là ta sẽ xậy dựng hệ thống SSO, ta chỉ điều hành user ở 1 chỗ, sau lúc đăng nhập vào hệ thống SSO này, token của nó sẽ được sử dụng cho nhiều dịch vụ không giống nhau đằng sau.Hình từ trang sphereinc.comAWS Cognito sẽ có 2 thành phần chính: user pool với identity pool.User pool thì giống như là 1 DB chứa user của chúng ta, ta sẽ tiến hành việc sign-up, sign-in với user pool này và nó sẽ trả cho ta 1 token, ở bài này ta sẽ sử dụng user pool.Identity pool thì cũng giống với user pool là đều dùng để chứa user, mà có 1 điểm dị biệt là token được trả về từ identity pool có được dùng để truy cập vào các dịch vụ khác của AWS.1 số cách khai triển CognitoFront-end + AWS CognitoỞ cách này FE sẽ tương tác trực tiếp với AWS Cognito sử dụng Aws Amplify framework.Ưu thế:Chỉ cần tạo AWS Cognito trên AWS, DevOps ko cần phải operation gì cả.AWS sẽ điều hành việc scale theo workload của user.Chỉ cần code FE.Nhược điểm:AWS Cognito phải tạo với public mode.Hoặc tạo với private mode thì secret key để kết nối với AWS Cognito phải lưu dưới source code của FE.Bảo mật kém.Front-end + API Gateway + AWS Lambda + AWS CognitoỞ cách này ta sẽ dùng lambda để tương tác với AWS Cognito, nếu có secret key thì ta sẽ lưu trên AWS và truyền vào Lambda như 1 biến env để bảo đảm security.Ưu thế:Bảo mật hơn so với mẫu hình FE + AWS Cognito.Ít phải operation, chỉ cần điều hành 1 function là login.Nhược điểm:AWS Lambda ở Region Singapore chỉ tiến hành tối đa được 500 concurrency 1 phút.Ko tự control được vấn đề scale.Sẽ có 1 vài cách khác nữa mà mình sẽ ko nói ở đây. Ở bài này vì ta đang học về Lambda nên ta sẽ xài cách Front-end + API Gateway + AWS Lambda + AWS Cognito 😂.Tạo user poolTruy cập AWS Console, kiếm tìm Cognito, bấm vào Cognito và bấm create user pool. Ta sẽ thấy UI như sau.Chỉ check vào Cognito user pool. Kéo xuống ở mục Cognito user pool sign-in options, check chọn email, bấm Next.Qua bước 2, ở phần Multi-factor authentication, chọn No MFA. Mấy mục còn lại để mặc định.Bước 3, bỏ check Enable self-registration, mấy mục còn lại để mặc định, bấm Next.Bước 4, ở mục Email chọn Send mail with Cognito.Bước 5, ở mục User pool name và App client name, bạn điền gì cũng được, mình điền tên là cognito-serverless-series.Ở mục Initial app client điền vào cognito-serverless-series.Ở mục Authentication flows, nhớ chọn thêm mục ALLOW_USER_PASSWORD_AUTH.Sau lúc điền xong bạn bấm Next để qua bước 6. Review và bấm Create user pool.Ok, sau lúc ta tạo xong user pool, bước tiếp theo ta sẽ dựng lại hệ thống ở bài trước và viết code cho Lambda login function, thêm vào API Gateway đường dẫn login, cập nhật lại đường dẫn create books thiết yếu authentication.Provisioning previous systemMình sẽ dùng terraform để tạo lại hệ thống, nếu các bạn muốn biết cách tạo bằng tay từng bước thì các bạn xem từ bài 2 nhé. Các bạn tải source code ở git repo này https://github.com/hoalongnatsu/serverless-series.git.Chuyển di đến thư mục bai-5/terraform-start. Ở file policies/lambda_policy.json, dòng “Resource”: “arn:aws:dynamodb:us-west-2:<ACCOUNT_ID>:table/books”, cập nhật lại <ACCOUNT_ID> với acc id của bạn. Xong sau đấy chạy câu lệnh.terraform init
terraform apply -auto-approve

Sau lúc Terraform chạy xong, nó sẽ in ra terminal API Gateway URL và Website URL.base_url = {
“api” = “https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging”
“web” = “http://serverless-series-spa.s3-website-us-west-2.amazonaws.com”
}

Sau lúc Terraform chạy xong ta sẽ có các resource sau đây.LambdaAPI GatewayDynamoDBS3 bucketTiếp theo ta copy dường dẫn api ở terminal trên, chuyển động đến folder bai-5/front-end, ở file .env-cmdrc, cập nhật lại trường staging.REACT_APP_API_URL: https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging. Sau đấy chạy câu lệnh.yarn install
yarn build:staging

Nó sẽ build code FE và tạo ra thư mục build, ta upload build thư mục lên trên S3 serverless-series-spa bucket.aws s3 cp build s3://serverless-series-spa/ –recursive

Oke, giờ ta truy cập http://serverless-series-spa.s3-website-us-west-2.amazonaws.com thì sẽ thấy được trang SPA của ta ở bài trước. Các bạn nên đọc các bài trước đấy để thông suốt về từng resource ở trên.Integrate CognitoCreate userTrước tiên để tương tác với Cognito thì ta sẽ tạo 1 user cho Cognito user pool, ta có thể viết 1 Lambda để tiến hành việc tạo user này, ở đây thì ta sẽ dùng AWS Console Web để tạo user cho nhanh. Truy cập Cognito, ở mục Users, bấm Create user.Ta sẽ thấy UI như sau.Chọn như trong hình, và điền vào email và password của user, xong bấm tạo.Ta sẽ thấy có dòng chữ màu xanh là Force change password, user được tạo trong Cognito buộc phải lúc đăng nhập lần trước tiên thì ta phải chỉnh sửa password của nó, thành ra ta mới có hàm change-password. Oke, sau lúc tạo user xong thì hiện thời ta sẽ thực hiện viết code.Implement Lambda change-passwordTa viết code cho hàm change-password, chuyển động đến thư mục bai-5/code.├── change-password
│ ├── build.sh
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── login
├── build.sh
├── go.mod
├── go.sum
└── main.go

Cập nhật lại file change-password/main.go như sau.package main

import (
“context”
“encoding/json”
“net/http”

“github.com/aws/aws-lambda-go/events”
“github.com/aws/aws-lambda-go/lambda”
“github.com/aws/aws-sdk-go-v2/aws”
“github.com/aws/aws-sdk-go-v2/config”
“github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider”
)

type Body struct {
Username string `json:”username”`
OldPassword string `json:”old_password”`
NewPassword string `json:”new_password”`
}

func ChangePassword(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var body Body
b64String, _ := base64.StdEncoding.DecodeString(req.Body)
rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

json.Unmarshal(bodyBytes, &body)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: “Error while retrieving AWS credentials”,
}, nil
}

cip := cognitoidentityprovider.NewFromConfig(cfg)
authInput := &cognitoidentityprovider.InitiateAuthInput{
AuthFlow: “USER_PASSWORD_AUTH”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”), // Should os.Getenv(“CLIENT_ID”)
AuthParameters: map[string]string{
“USERNAME”: body.Username,
“PASSWORD”: body.OldPassword,
},
}
authResp, err := cip.InitiateAuth(context.TODO(), authInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

challengeInput := &cognitoidentityprovider.RespondToAuthChallengeInput{
ChallengeName: “NEW_PASSWORD_REQUIRED”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”),
ChallengeResponses: map[string]string{
“USERNAME”: body.Username,
“NEW_PASSWORD”: body.NewPassword,
},
Session: authResp.Session,
}
challengeResp, err := cip.RespondToAuthChallenge(context.TODO(), challengeInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

res, _ := json.Marshal(challengeResp)
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
“Content-Type”: “application/json”,
“Access-Control-Allow-Origin”: “*”,
},
Body: string(res),
}, nil
}

func main() {
lambda.Start(ChangePassword)
}

Trước nhất, vì ở bài trước ta đã thêm vào Binary Media Types trị giá */*.Nên request sẽ được encoded và ta chẳng thể lấy req.body ra bằng hàm json.Unmarshal([]byte(req.Body), &body) được, nhưng mà ta phải làm như sau.var body Body
b64String, _ := base64.StdEncoding.DecodeString(req.Body)
rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

json.Unmarshal(bodyBytes, &body)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

Để tương tác với Cognito, trước tiên ta sẽ tạo cognito client bằng hàm cip := cognitoidentityprovider.NewFromConfig(cfg). Sau đấy, ta thực hiện lấy thông tin chứng nhận của user bằng đoạn code.authInput := &cognitoidentityprovider.InitiateAuthInput{
AuthFlow: “USER_PASSWORD_AUTH”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”), // Should os.Getenv(“CLIENT_ID”)
AuthParameters: map[string]string{
“USERNAME”: body.Username,
“PASSWORD”: body.OldPassword,
},
}
authResp, err := cip.InitiateAuth(context.TODO(), authInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

Sau lúc lấy được thông tin của user rồi, ta sẽ thực hiện đổi mật khẩu bằng đoạn code.challengeInput := &cognitoidentityprovider.RespondToAuthChallengeInput{
ChallengeName: “NEW_PASSWORD_REQUIRED”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”),
ChallengeResponses: map[string]string{
“USERNAME”: body.Username,
“NEW_PASSWORD”: body.NewPassword,
},
Session: authResp.Session,
}
challengeResp, err := cip.RespondToAuthChallenge(context.TODO(), challengeInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

Sau đấy ta trả về kết quả chứng nhận của user cho client. Trị giá ClientId, ta lấy ở phần App integration trong Cognito.Lúc làm thực tiễn các bạn nên dùng os.Getenv(“CLIENT_ID”) thay vì điền trực tiếp trong function. Sau lúc viết code xong thì ta init package và update lại AWS Lambda function.go get
sh build.sh
aws lambda update-function-code –function-name change_password –zip-file fileb://change-password.zip –region us-west-2

Giờ ta sẽ rà soát thử hàm change-password của ta chạy đúng ko.curl -sX POST -d ‘{“username”:”hoalongnatsu@gmail.com”, “old_password”: “12345678@aA”, “new_password”: “87654321@bB”}’ https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging/change-password

Kết quả trả về.{
“AuthenticationResult”: {
“AccessToken”: “eyJ…”,
“ExpiresIn”: 3600,
“IdToken”: “eyJ…”,
“NewDeviceMetadata”: null,
“RefreshToken”: “eyJ…”,
“TokenType”: “Bearer”
},
“ChallengeName”: “”,
“ChallengeParameters”: {},
“Session”: null,
“ResultMetadata”: {}
}

Nếu bạn thấy kết quả trên thì hàm của ta đã chạy đúngImplement Lambda loginTiếp theo ta sẽ viết code cho hàm login, cập nhật lại file login/main.go như sau.package main

import (
“context”
“encoding/json”
“net/http”

“github.com/aws/aws-lambda-go/events”
“github.com/aws/aws-lambda-go/lambda”
“github.com/aws/aws-sdk-go-v2/aws”
“github.com/aws/aws-sdk-go-v2/config”
“github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider”
)

type Body struct {
Username string `json:”username”`
Password string `json:”password”`
}

func login(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var body Body
b64String, _ := base64.StdEncoding.DecodeString(req.Body)
rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

json.Unmarshal(bodyBytes, &body)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: “Error while retrieving AWS credentials”,
}, nil
}

cip := cognitoidentityprovider.NewFromConfig(cfg)
authInput := &cognitoidentityprovider.InitiateAuthInput{
AuthFlow: “USER_PASSWORD_AUTH”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”), // Should os.Getenv(“CLIENT_ID”)
AuthParameters: map[string]string{
“USERNAME”: body.Username,
“PASSWORD”: body.Password,
},
}
authResp, err := cip.InitiateAuth(context.TODO(), authInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

res, _ := json.Marshal(authResp)
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
“Content-Type”: “application/json”,
“Access-Control-Allow-Origin”: “*”,
},
Body: string(res),
}, nil
}

func main() {
lambda.Start(login)
}

Hàm login thì gần giống với hàm change-password. Sau lúc cập nhật code xong thì ta init package và update lại AWS Lambda function.go get
sh build.sh
aws lambda update-function-code –function-name login –zip-file fileb://login.zip –region us-west-2

Ta rà soát thử hàm login của ta.curl -sX POST -d ‘{“username”:”hoalongnatsu@gmail.com”, “password”: “87654321@bB”}’ https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging/login

Kết quả.{
“AuthenticationResult”: {
“AccessToken”: “eyJ…”,
“ExpiresIn”: 3600,
“IdToken”: “eyJ…”,
“NewDeviceMetadata”: null,
“RefreshToken”: “eyJ…”,
“TokenType”: “Bearer”
},
“ChallengeName”: “”,
“ChallengeParameters”: {},
“Session”: null,
“ResultMetadata”: {}
}

Oke, hàm của ta đã chạy được 😁. Trị giá ở trong trường AuthenticationResult sẽ được sử dụng lúc ta cần gọi API nhưng mà có authentication. Ở phần này thì ta chỉ làm đến đoạn login và change-password.Kết luậnVậy là ta đã mày mò xong Cognito là gì, và cách integrate Lambda với nó. Sử dụng Cognito sẽ giúp ta dễ dãi hơn nhiều trong việc điều hành và chứng nhận user. Nếu có thắc mắc hoặc cần giảng giải rõ thêm chỗ nào thì các bạn có thể hỏi dưới phần comment. Ở phần 2 mình sẽ nói tiếp về cách integrate API Gateway với Cognito để nó tự động chính xác user cho ta.Hứa gặp mọi đứa ở phần 2 của bài này,
#Serverless #Series #Golang #Bài #Authentication #Amazon #Cognito #Part #tháng #lượt #xem #Golang #DevOps #AWS #Đầu #mục #bài #viếtBài #viết #liên #quanKhoá #học #hay
[rule_2_plain] #Serverless #Series #Golang #Bài #Authentication #Amazon #Cognito #Part #tháng #lượt #xem #Golang #DevOps #AWS #Đầu #mục #bài #viếtBài #viết #liên #quanKhoá #học #hay
[rule_2_plain] #Serverless #Series #Golang #Bài #Authentication #Amazon #Cognito #Part #tháng #lượt #xem #Golang #DevOps #AWS #Đầu #mục #bài #viếtBài #viết #liên #quanKhoá #học #hay
[rule_3_plain]

#Serverless #Series #Golang #Bài #Authentication #Amazon #Cognito #Part #tháng #lượt #xem #Golang #DevOps #AWS #Đầu #mục #bài #viếtBài #viết #liên #quanKhoá #học #hay
Giới thiệuChào các bạn đến với series về Serverless, ở bài trước chúng ta đã nói về cách sử dụng S3 để hosting 1 trang Single Page Application và lưu trữ hình ảnh, cách sử dụng Lambda để upload file lên trên S3. Và do tất cả url của ta đều là public nên bất kỳ người nào cũng có thể vào trang create để tạo dữ liệu và upload file lên trên S3 cả, nhưng mà ta sẽ ko muốn tương tự. Nên ở bài này chúng ta sẽ mày mò về 1 thành phần tiếp theo được sử dụng để tiến hành việc authentication và điều hành user, là AWS Cognito.Hệ thống nhưng mà ta sẽ xây dựng như sau.Ở bài trước, ta đã dựng được phần API Gateway + Lambda + DynamoDB + S3.Ở bài này ta sẽ thêm vào AWS Cognito.AWS CognitoAWS Cognito là 1 dịch vụ của AWS nhưng mà được sử dụng cho việc chứng nhận (authentication) user. Sử dụng nó sẽ giúp ta trong việc xây dựng luồng sign-in, sign-up, verify email, change password, restart password, v…v… 1 cách đơn giản hơn, thay vì ta phải tự xây dựng DB cho user và tự làm nhiều thứ như JWT, hash password, send mail verify, v…v…1 usecase bình thường của AWS Cognito là sử dụng cho hệ thống SSO (single sign-on). Thí dụ là hệ thống của ta có 3 dịch vụ không giống nhau, mỗi dịch vụ đều có 1 trang đăng nhập và DB user riêng, lúc hệ thống ta tăng trưởng lên đến mấy chục dịch vụ, việc xây dựng mỗi hệ thống đều sử dụng thằng authentication của riêng nó sẽ khiến ta rất khó để điều hành user. Biện pháp là ta sẽ xậy dựng hệ thống SSO, ta chỉ điều hành user ở 1 chỗ, sau lúc đăng nhập vào hệ thống SSO này, token của nó sẽ được sử dụng cho nhiều dịch vụ không giống nhau đằng sau.Hình từ trang sphereinc.comAWS Cognito sẽ có 2 thành phần chính: user pool với identity pool.User pool thì giống như là 1 DB chứa user của chúng ta, ta sẽ tiến hành việc sign-up, sign-in với user pool này và nó sẽ trả cho ta 1 token, ở bài này ta sẽ sử dụng user pool.Identity pool thì cũng giống với user pool là đều dùng để chứa user, mà có 1 điểm dị biệt là token được trả về từ identity pool có được dùng để truy cập vào các dịch vụ khác của AWS.1 số cách khai triển CognitoFront-end + AWS CognitoỞ cách này FE sẽ tương tác trực tiếp với AWS Cognito sử dụng Aws Amplify framework.Ưu thế:Chỉ cần tạo AWS Cognito trên AWS, DevOps ko cần phải operation gì cả.AWS sẽ điều hành việc scale theo workload của user.Chỉ cần code FE.Nhược điểm:AWS Cognito phải tạo với public mode.Hoặc tạo với private mode thì secret key để kết nối với AWS Cognito phải lưu dưới source code của FE.Bảo mật kém.Front-end + API Gateway + AWS Lambda + AWS CognitoỞ cách này ta sẽ dùng lambda để tương tác với AWS Cognito, nếu có secret key thì ta sẽ lưu trên AWS và truyền vào Lambda như 1 biến env để bảo đảm security.Ưu thế:Bảo mật hơn so với mẫu hình FE + AWS Cognito.Ít phải operation, chỉ cần điều hành 1 function là login.Nhược điểm:AWS Lambda ở Region Singapore chỉ tiến hành tối đa được 500 concurrency 1 phút.Ko tự control được vấn đề scale.Sẽ có 1 vài cách khác nữa mà mình sẽ ko nói ở đây. Ở bài này vì ta đang học về Lambda nên ta sẽ xài cách Front-end + API Gateway + AWS Lambda + AWS Cognito 😂.Tạo user poolTruy cập AWS Console, kiếm tìm Cognito, bấm vào Cognito và bấm create user pool. Ta sẽ thấy UI như sau.Chỉ check vào Cognito user pool. Kéo xuống ở mục Cognito user pool sign-in options, check chọn email, bấm Next.Qua bước 2, ở phần Multi-factor authentication, chọn No MFA. Mấy mục còn lại để mặc định.Bước 3, bỏ check Enable self-registration, mấy mục còn lại để mặc định, bấm Next.Bước 4, ở mục Email chọn Send mail with Cognito.Bước 5, ở mục User pool name và App client name, bạn điền gì cũng được, mình điền tên là cognito-serverless-series.Ở mục Initial app client điền vào cognito-serverless-series.Ở mục Authentication flows, nhớ chọn thêm mục ALLOW_USER_PASSWORD_AUTH.Sau lúc điền xong bạn bấm Next để qua bước 6. Review và bấm Create user pool.Ok, sau lúc ta tạo xong user pool, bước tiếp theo ta sẽ dựng lại hệ thống ở bài trước và viết code cho Lambda login function, thêm vào API Gateway đường dẫn login, cập nhật lại đường dẫn create books thiết yếu authentication.Provisioning previous systemMình sẽ dùng terraform để tạo lại hệ thống, nếu các bạn muốn biết cách tạo bằng tay từng bước thì các bạn xem từ bài 2 nhé. Các bạn tải source code ở git repo này https://github.com/hoalongnatsu/serverless-series.git.Chuyển di đến thư mục bai-5/terraform-start. Ở file policies/lambda_policy.json, dòng “Resource”: “arn:aws:dynamodb:us-west-2:<ACCOUNT_ID>:table/books”, cập nhật lại <ACCOUNT_ID> với acc id của bạn. Xong sau đấy chạy câu lệnh.terraform init
terraform apply -auto-approve

Sau lúc Terraform chạy xong, nó sẽ in ra terminal API Gateway URL và Website URL.base_url = {
“api” = “https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging”
“web” = “http://serverless-series-spa.s3-website-us-west-2.amazonaws.com”
}

Sau lúc Terraform chạy xong ta sẽ có các resource sau đây.LambdaAPI GatewayDynamoDBS3 bucketTiếp theo ta copy dường dẫn api ở terminal trên, chuyển động đến folder bai-5/front-end, ở file .env-cmdrc, cập nhật lại trường staging.REACT_APP_API_URL: https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging. Sau đấy chạy câu lệnh.yarn install
yarn build:staging

Nó sẽ build code FE và tạo ra thư mục build, ta upload build thư mục lên trên S3 serverless-series-spa bucket.aws s3 cp build s3://serverless-series-spa/ –recursive

Oke, giờ ta truy cập http://serverless-series-spa.s3-website-us-west-2.amazonaws.com thì sẽ thấy được trang SPA của ta ở bài trước. Các bạn nên đọc các bài trước đấy để thông suốt về từng resource ở trên.Integrate CognitoCreate userTrước tiên để tương tác với Cognito thì ta sẽ tạo 1 user cho Cognito user pool, ta có thể viết 1 Lambda để tiến hành việc tạo user này, ở đây thì ta sẽ dùng AWS Console Web để tạo user cho nhanh. Truy cập Cognito, ở mục Users, bấm Create user.Ta sẽ thấy UI như sau.Chọn như trong hình, và điền vào email và password của user, xong bấm tạo.Ta sẽ thấy có dòng chữ màu xanh là Force change password, user được tạo trong Cognito buộc phải lúc đăng nhập lần trước tiên thì ta phải chỉnh sửa password của nó, thành ra ta mới có hàm change-password. Oke, sau lúc tạo user xong thì hiện thời ta sẽ thực hiện viết code.Implement Lambda change-passwordTa viết code cho hàm change-password, chuyển động đến thư mục bai-5/code.├── change-password
│ ├── build.sh
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── login
├── build.sh
├── go.mod
├── go.sum
└── main.go

Cập nhật lại file change-password/main.go như sau.package main

import (
“context”
“encoding/json”
“net/http”

“github.com/aws/aws-lambda-go/events”
“github.com/aws/aws-lambda-go/lambda”
“github.com/aws/aws-sdk-go-v2/aws”
“github.com/aws/aws-sdk-go-v2/config”
“github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider”
)

type Body struct {
Username string `json:”username”`
OldPassword string `json:”old_password”`
NewPassword string `json:”new_password”`
}

func ChangePassword(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var body Body
b64String, _ := base64.StdEncoding.DecodeString(req.Body)
rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

json.Unmarshal(bodyBytes, &body)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: “Error while retrieving AWS credentials”,
}, nil
}

cip := cognitoidentityprovider.NewFromConfig(cfg)
authInput := &cognitoidentityprovider.InitiateAuthInput{
AuthFlow: “USER_PASSWORD_AUTH”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”), // Should os.Getenv(“CLIENT_ID”)
AuthParameters: map[string]string{
“USERNAME”: body.Username,
“PASSWORD”: body.OldPassword,
},
}
authResp, err := cip.InitiateAuth(context.TODO(), authInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

challengeInput := &cognitoidentityprovider.RespondToAuthChallengeInput{
ChallengeName: “NEW_PASSWORD_REQUIRED”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”),
ChallengeResponses: map[string]string{
“USERNAME”: body.Username,
“NEW_PASSWORD”: body.NewPassword,
},
Session: authResp.Session,
}
challengeResp, err := cip.RespondToAuthChallenge(context.TODO(), challengeInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

res, _ := json.Marshal(challengeResp)
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
“Content-Type”: “application/json”,
“Access-Control-Allow-Origin”: “*”,
},
Body: string(res),
}, nil
}

func main() {
lambda.Start(ChangePassword)
}

Trước nhất, vì ở bài trước ta đã thêm vào Binary Media Types trị giá */*.Nên request sẽ được encoded và ta chẳng thể lấy req.body ra bằng hàm json.Unmarshal([]byte(req.Body), &body) được, nhưng mà ta phải làm như sau.var body Body
b64String, _ := base64.StdEncoding.DecodeString(req.Body)
rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

json.Unmarshal(bodyBytes, &body)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

Để tương tác với Cognito, trước tiên ta sẽ tạo cognito client bằng hàm cip := cognitoidentityprovider.NewFromConfig(cfg). Sau đấy, ta thực hiện lấy thông tin chứng nhận của user bằng đoạn code.authInput := &cognitoidentityprovider.InitiateAuthInput{
AuthFlow: “USER_PASSWORD_AUTH”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”), // Should os.Getenv(“CLIENT_ID”)
AuthParameters: map[string]string{
“USERNAME”: body.Username,
“PASSWORD”: body.OldPassword,
},
}
authResp, err := cip.InitiateAuth(context.TODO(), authInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

Sau lúc lấy được thông tin của user rồi, ta sẽ thực hiện đổi mật khẩu bằng đoạn code.challengeInput := &cognitoidentityprovider.RespondToAuthChallengeInput{
ChallengeName: “NEW_PASSWORD_REQUIRED”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”),
ChallengeResponses: map[string]string{
“USERNAME”: body.Username,
“NEW_PASSWORD”: body.NewPassword,
},
Session: authResp.Session,
}
challengeResp, err := cip.RespondToAuthChallenge(context.TODO(), challengeInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

Sau đấy ta trả về kết quả chứng nhận của user cho client. Trị giá ClientId, ta lấy ở phần App integration trong Cognito.Lúc làm thực tiễn các bạn nên dùng os.Getenv(“CLIENT_ID”) thay vì điền trực tiếp trong function. Sau lúc viết code xong thì ta init package và update lại AWS Lambda function.go get
sh build.sh
aws lambda update-function-code –function-name change_password –zip-file fileb://change-password.zip –region us-west-2

Giờ ta sẽ rà soát thử hàm change-password của ta chạy đúng ko.curl -sX POST -d ‘{“username”:”hoalongnatsu@gmail.com”, “old_password”: “12345678@aA”, “new_password”: “87654321@bB”}’ https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging/change-password

Kết quả trả về.{
“AuthenticationResult”: {
“AccessToken”: “eyJ…”,
“ExpiresIn”: 3600,
“IdToken”: “eyJ…”,
“NewDeviceMetadata”: null,
“RefreshToken”: “eyJ…”,
“TokenType”: “Bearer”
},
“ChallengeName”: “”,
“ChallengeParameters”: {},
“Session”: null,
“ResultMetadata”: {}
}

Nếu bạn thấy kết quả trên thì hàm của ta đã chạy đúngImplement Lambda loginTiếp theo ta sẽ viết code cho hàm login, cập nhật lại file login/main.go như sau.package main

import (
“context”
“encoding/json”
“net/http”

“github.com/aws/aws-lambda-go/events”
“github.com/aws/aws-lambda-go/lambda”
“github.com/aws/aws-sdk-go-v2/aws”
“github.com/aws/aws-sdk-go-v2/config”
“github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider”
)

type Body struct {
Username string `json:”username”`
Password string `json:”password”`
}

func login(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var body Body
b64String, _ := base64.StdEncoding.DecodeString(req.Body)
rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

json.Unmarshal(bodyBytes, &body)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusBadRequest,
Body: err.Error(),
}, nil
}

cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: “Error while retrieving AWS credentials”,
}, nil
}

cip := cognitoidentityprovider.NewFromConfig(cfg)
authInput := &cognitoidentityprovider.InitiateAuthInput{
AuthFlow: “USER_PASSWORD_AUTH”,
ClientId: aws.String(“6jk1bh3me5h1onmbjhqalmtpp8”), // Should os.Getenv(“CLIENT_ID”)
AuthParameters: map[string]string{
“USERNAME”: body.Username,
“PASSWORD”: body.Password,
},
}
authResp, err := cip.InitiateAuth(context.TODO(), authInput)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: http.StatusInternalServerError,
Body: err.Error(),
}, nil
}

res, _ := json.Marshal(authResp)
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
“Content-Type”: “application/json”,
“Access-Control-Allow-Origin”: “*”,
},
Body: string(res),
}, nil
}

func main() {
lambda.Start(login)
}

Hàm login thì gần giống với hàm change-password. Sau lúc cập nhật code xong thì ta init package và update lại AWS Lambda function.go get
sh build.sh
aws lambda update-function-code –function-name login –zip-file fileb://login.zip –region us-west-2

Ta rà soát thử hàm login của ta.curl -sX POST -d ‘{“username”:”hoalongnatsu@gmail.com”, “password”: “87654321@bB”}’ https://kvpspx1bw0.execute-api.us-west-2.amazonaws.com/staging/login

Kết quả.{
“AuthenticationResult”: {
“AccessToken”: “eyJ…”,
“ExpiresIn”: 3600,
“IdToken”: “eyJ…”,
“NewDeviceMetadata”: null,
“RefreshToken”: “eyJ…”,
“TokenType”: “Bearer”
},
“ChallengeName”: “”,
“ChallengeParameters”: {},
“Session”: null,
“ResultMetadata”: {}
}

Oke, hàm của ta đã chạy được 😁. Trị giá ở trong trường AuthenticationResult sẽ được sử dụng lúc ta cần gọi API nhưng mà có authentication. Ở phần này thì ta chỉ làm đến đoạn login và change-password.Kết luậnVậy là ta đã mày mò xong Cognito là gì, và cách integrate Lambda với nó. Sử dụng Cognito sẽ giúp ta dễ dãi hơn nhiều trong việc điều hành và chứng nhận user. Nếu có thắc mắc hoặc cần giảng giải rõ thêm chỗ nào thì các bạn có thể hỏi dưới phần comment. Ở phần 2 mình sẽ nói tiếp về cách integrate API Gateway với Cognito để nó tự động chính xác user cho ta.Hứa gặp mọi đứa ở phần 2 của bài này,

Related Articles

Trả lời

Email của bạn sẽ không được hiển thị công khai.

Back to top button