本文主要分为三个部分:

1、描述 cognito 涉及的专业术语 以及 交互流程

2、.net 集成的代码

3、感想

* 阅读提示 :鼠标悬停在 章节标题 上可见 文章目录

1. Cognito 概念

1.1 关键词

进入 Amazon Cognito,会先看到 user pool 的列表

cognito
亚马逊(Amazon)云 提供的一种用户管理服务,简化用户注册、登录和授权、鉴权相关的服务操作。
可以在 cognito 上创建用户池,管理用户注册、登录和鉴权相关的问题;
可以创建 identity pool 对用户进行授权相关的约束;
支持多种身份验证方式,例如 用户 / 密码 登录,社交帐号( Facebook,Google 等)登录,企业身份供应商
 
user pool
用户池,多租户 / 不同的服务供应商,拥有自己的用户群体;
用户池支持用户注册、登录、鉴权、账号恢复等。
identity pool
资格授权池,可以提供一些 AWS 的资格认证到通过验证的用户上
app client
用户池所关联的应用服务端,必须要配置到相关的用户池上才允许访问该用户池

选定一个用户池,可以查看该用户池的信息,左侧栏见其 功能 / 配置

user pool 功能 / 配置 目录列表

* 为了降低描述的复杂度,此处仅做最小配置展开

* 加粗为需要重点关注;此处不展开 IdentityPool 以及 userPool.Security 和 userPool.Branding

Seconcd

Third

Description

Remark

User Pool

Overview

用户池的基本信息

集成时需要找到这个用户池的 ID、ARN 等信息

Applications

app client

配置可访问该用户池的应用端信息

在此处可以限制一些读写的权限

User Management

Users

用户池里面的每个用户

相当于用户表,可以自定义用户属性 custom user attribute

Groups

用户分组,主要用于指定某个组内的用户可以享有某些权限

按需求来,可选配置

Authentication

Authentication methods

用户验证方式的配置

可以在此处配置密码验证 或 通过短信验证 或 通过邮箱验证

Sign-in

用户登录时的相关配置

例如密码复杂度,登录方式

Sign-up

用户注册时的相关配置

Social and external providers

通过第三方登录的配置,像是通过 Facebook, Google, Amazon or Apple..等其他供应商来授权

Extensions

通过一些自定义的验证行为,触发 AWS lambda function。此处是触发器的配置。

此处的触发条件是 cognito 规定好的。也即仅可以配置:要不要触发,触发哪一个function

Security

AWS WAF

Threat protection

Log streaming

Branding

Domain

Managed login

Message templates

Identity Pool

Overview

Authentication Providers

Roles

IAM Policies

Data Synchronization

Cognito 主要是做 用户管理 的事情,它支持通过 密码、手机短信、邮箱地址、其他企业供应商(例如 facebook、apple account 等) 等方式授权 token,

本文主要描述 自定义校验 custom authentication 的方式,此处要求在 user pool 中配置 3 个 AWS lambda function ,cognito 将会触发它的执行。

Lambda function 也是 Amazon web service 其中之一,此处可以简单地把其当作一个AWS的 api 方法。

这里涉及到 3 个触发器:define auth challengecreate auth challengeverify auth challenge

Custom authentication trigger type

Define Auth Challenge

custom authentication 的第一步,这里会返回 custom challenge name 到 cognito;同时也可以基于用户逻辑,直接在此处发布 token 。

Create Auth Challenge

custom authentication 流程的第二步,实现自定义身份验证的步骤。通常在这一步中写入验证的答案,验证的问题可以是 captchas 或者其他安全问题 或 验证码 等。

Verify Auth Challenge

custom authentication 的第三步,校验用户输入的答案与在 create auth challenge 中预设的答案是否一致

下文将描述我们的代码会如何与 cognito 交互,又是如何触发对应的 lambda function。

1.2. 流程图

登录步骤 1:输入账号

  • 前端第一次调用接口,是提交登录的请求;
  • 后端调用 AWSSDK.CognitoIdentifyProvider 中 cognitoClient.InitiateAuthAsync 方法,这个方法是 cognito 支持的自定义验证方式,它会自动触发到 define auth challengecreate auth challenge
  • 此处发送短信的方式是后端去调用了一个 AWS Lambda Function

登录步骤 2:输入验证码

  • 前端第二次调用的接口,是提交登录的验证码
  • 后端会把验证码发送给 user pool 去做校验,通过了校验才能成功授权返回 token

* AWS Lambda Triggers

场景

SDK Function

Trigger the Lambda

sign-in

登录

InitiateAuthAsync

Define Auth Challenge, Create Auth Challenge

RespondToAuthChallengeAsync

Verify Auth Challenge

sign-up

注册

SignUpAsync

Custom message trigger

ConfirmSignUpAsync

Define auth challenge

  • 这个 trigger 用于决定下一个验证步骤,可以在这里配置走向密码验证或者自定义的验证方式。
  • 基于正确的 user session 上,它进一步定义验证的流程

例如,用户通过密码验证之后,将进入到手机短信验证

// cognito 将会触发到配置在 user pool 上的 lambda trigger,event 是它的入参
exports.handler = async (event) => {
if (event.request.session.length === 1 && event.request.session[0].challengeName === 'SRP_A') {
     // 是否直接发布 token
event.response.issueTokens = false;
// 是否验证失败
event.response.failAuthentication = false;
// 下一个验证方式的名字
event.response.challengeName = 'PASSWORD_VERIFIER'; } else if (event.request.session.length === 2 && event.request.session[1].challengeName === 'PASSWORD_VERIFIER' && event.request.session[1].challengeResult === true) {
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE'; } else if (event.request.session.length === 3 && event.request.session[2].challengeName === 'CUSTOM_CHALLENGE' && event.request.session[2].challengeResult === true) {
event.response.issueTokens = true;
event.response.failAuthentication = false; } else {
event.response.issueTokens = false;
event.response.failAuthentication = true;
} return event;
};

Create auth challenge

  • 一旦 Define Auth Challenge 触发器指明使用自定义验证,那么 Create Auth Challenge 就会被触发去生成验证的内容。
  • 在这个步骤中所创建的校验内容,是用户必须要答复的。例如 发到 SMS、Email 上的验证码,或者 CAPTCHA 之类的验证问题。

例如在 Create Auth Challenge 上设定一个将发送到 Email 的验证码(one-time password,简称 OTP),

此时 Amazon Cognito 其实会存储这个 event 内容,关联到用户的 session 和 用于  Verify Auth Challenge 步骤作为校验的答案(这里是验证码)

lambda function 实现示例:

   exports.handler = async (event) => {
if (event.request.challengeName === 'CUSTOM_CHALLENGE') {
const otp = '654321'; // Generate or set your OTP here
event.response.publicChallengeParameters = { otp: 'Enter the OTP sent to your email' };
event.response.privateChallengeParameters = { otp: otp };
event.response.challengeMetadata = 'CUSTOM_CHALLENGE';
}
return event;
};

Verify auth challenge

  • 该触发器在用户提交验证码之后被触发
  • 该触发器接收到用户输入的 验证码 后会与 Create Auth Challenge 中的验证码相互匹配。如果校验正确,cognito 会处理返回的 event 并生成 token

lambda function 实现示例:

   // TriggerSource is VerifyAuthChallengeResponse_Authentication
exports.handler = async (event) => {
const expectedOtp = event.request.privateChallengeParameters.otp;
const userResponse = event.request.challengeAnswer;
event.response.answerCorrect = userResponse === expectedOtp;
return event;
};

2. 集成到 .net 8 api

.NET 中需要使用 AWS SDK 提供的 API 来与 Cognito 进行交互,

引用包:AWSSDK.CognitoIdentityProvider ,下文代码使用的版本是 3.7.1.85

<PackageReference Include="AWSSDK.CognitoIdentityProvider" Version="3.7.1.85" />

添加引用

using Amazon.CognitoIdentityProvider;
using Amazon.CognitoIdentityProvider.Model;

cognitoClient 初始化:

var AWSregion = "ap-xxxxx-x";
_cognitoClient = new AmazonCognitoIdentityProviderClient(Amazon.RegionEndpoint.GetBySystemName(AWSregion));

2.1.1 注册 [signup]

public async Task<SignUpResponse> SignupAsync(SignUpDto user)
{
var userName = string.IsNullOrEmpty(user.Email) ? user.PhoneNumber : user.Email;
var request = new SignUpRequest
{
// 在 cognito user pool 中配置允许访问该 user pool 的 client 后,可以在cognito 上查看到这个 clientId
ClientId = _clientId, // cognito 会校验代码复杂度,需要在 user pool 中配置;但此示例中密码是没有作用的
Password = "carcar@2024", // 此处可以填手机号或邮箱地址; cognito user pool 中显示的 userName 是 user pool 用户管理意义上的 uuid
Username = userName
}; // 自定义的 user attribute
var nameAttribute = new AttributeType
{
Name = "name",
Value = user.Name
};
request.UserAttributes.Add(nameAttribute); // 自定义的 user attribute
var emailAttribute = new AttributeType
{
Name = "email",
Value = user.Email
};
request.UserAttributes.Add(emailAttribute); return await _cognitoClient.SignUpAsync(request);
}

可配置密码复杂度配置

可以配置对 user attribute 的必填要求

查看 user attribute

2.1.2 注册验证 [signup-confirm]

 var confirmSignUpRequest = new ConfirmSignUpRequest
{
ClientId = "client id",
Username = "user phone / email",
ConfirmationCode = "verification code from sms or email"
}; var confirmSignUpResponse = await cognitoClient.ConfirmSignUpAsync(confirmSignUpRequest);
Console.WriteLine($"User {confirmSignUpResponse.UserConfirmed} confirmed successfully.")

2.1.3 重新发送验证码 [resend-confirmation]

// userName 可以是 cognito UserName,又或者是注册时写入的 userName 即邮箱或手机号
public async Task ResendConfirmationAsync(string userName)
{
var request = new ResendConfirmationCodeRequest
{
ClientId = _clientId,
Username = userName
}; var response = await _cognitoClient.ResendConfirmationCodeAsync(request);
}

2.2.1 登录 [signin]

var authRequest = new InitiateAuthRequest
{
ClientId = "the client id which configures in the user pool",
AuthFlow = AuthFlowType.CUSTOM_AUTH,
AuthParameters = new Dictionary<string, string>
{
{ "USERNAME", "your_username_or_phone_or_email" }
}
}; var authResponse = await cognitoClient.InitiateAuthAsync(authRequest);

2.2.2 登录验证 [signin-confirm]

校验验证码时,调用 RespondToAuthChallengeAsync

 var respondToAuthChallengeRequest = new RespondToAuthChallengeRequest
{
ChallengeName = authResponse.ChallengeName,
ClientId = "your_client_id",
ChallengeResponses = new Dictionary<string, string>
{
{ "USERNAME", "your_username_or_phone_or_email" },
{ "SMS_MFA_CODE", "user_received_code" }
},
Session = authResponse.Session
}; var challengeResponse = await cognitoClient.RespondToAuthChallengeAsync(respondToAuthChallengeRequest); var idToken = challengeResponse.AuthenticationResult.IdToken;

2.3 token 验证

在 Program 或 StartUp 的配置文件中,需要配置 Authentication 中间件

* token 参数说明 - 点此跳转查看

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = $"https://cognito-idp.{AWSregion}.amazonaws.com/{userPoolId}",
// ValidAudience = {userPoolId},
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
// Get JsonWebKeySet from AWS
var json = new WebClient().DownloadString($"https://cognito-idp.{AWSregion}.amazonaws.com/{userPoolId}/.well-known/jwks.json");
// Deserialize the result
var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
// Cast the result to be the type expected by IssuerSigningKeyResolver
return (IEnumerable<SecurityKey>)keys;
}
};
});

3. 为什么写这一篇文章

很多开发可能和我一样,没有成为一名云上工程师,或者说项目里并没有使用 Amazon Web Service,那么对于如何集成 AWS 可能是不感兴趣的。

那我为什么写这篇文章?

1、国内对 AWS 的应用较少,AWS 相关的资料大多是英文的,其实解读下来真的挺花时间。

2、现在新颖的技术层出不穷,我的希望是在探索 cognito 的过程中,建立一个快速理解的方法论。

这背后考验的是专业知识以及逻辑梳理能力。我们一定是:知其所以然,才能应对多变的表象。

当然,这里我指的是对云服务如何集成传统应用,或者说“我的应用要怎么上云”。

3、借此机会看一下云服务的设计,现在都怎么玩的。

理解云服务的应用不单止为我们多提供一种解决方案,在排查集成云集成中产生的问题,也会有所启发。

是否能够以小见大,找到表象的本质?

在既定的解决方案架构里面找到可以拓展的共性,能不能经验迁移?

无论如何,希望这篇文章对你有所收益。

References

[1] Amazon Cognito Identity Provider examples using AWS SDK for .NET

[2] Authentication flow examples with .NET for Amazon Cognito

[3] Authenticating users in ASP.NET Core MVC using Amazon Cognito

[4] Securing ASP.NET Core API with JWT Token using AWS Cognito

.net 8 C# 集成 AWS Cognito SMS/Email 注册与登录的更多相关文章

  1. 在aws ec2上使用root用户登录

    aws ec2默认是使用ec2-user账号登陆的,对很多文件夹是没有权限的.如何使用root账号执行命令就是一个问题了.解决办法如下: 1.根据官网提供的方法登录连接到EC2服务器(官网推荐wind ...

  2. Open Phone, SMS, Email, Skype and Browser apps of Android in Unity3d

    最近项目需要使用Android的一些基本功能,写插件各种悲剧,google了一下,如获至宝.Nice ! string url = String.Format("tel:{0}", ...

  3. SpringCloud微服务实战——搭建企业级开发框架(二十四):集成行为验证码和图片验证码实现登录功能

    随着近几年技术的发展,人们对于系统安全性和用户体验的要求越来越高,大多数网站系统都逐渐采用行为验证码来代替图片验证码.GitEgg-Cloud集成了开源行为验证码组件和图片验证码,并在系统中添加可配置 ...

  4. 可选择Email和用户名登录的代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. aws linux主机root帐号登录

    默认情况下,aws主机必须使用pem密码文件并且以ec2-user用户登录系统,之后很多操作都必须用sudo来以root权限执行操作,显得比较麻烦. 以下来自知乎的一个问答,亲测ok ## AWS E ...

  6. SpringBoot集成Spring Security(2)——自动登录

    在上一章:SpringBoot集成Spring Security(1)——入门程序中,我们实现了入门程序,本篇为该程序加上自动登录的功能. 文章目录 一.修改login.html二.两种实现方式 2. ...

  7. 设置ecShop网店用户名和email均可登录

    修改user.php文件,如果您网站的该文件修改过,按照下面的修改说明修改文件. 查找代码:elseif ($action == 'act_login') 在:$back_act = isset($_ ...

  8. Gitlab CI持续集成 - GitLab Runner 安装与注册

    GitLab Runner安装 需要添加gitlab官方库: # For Debian/Ubuntu/Mint curl -L https://packages.gitlab.com/install/ ...

  9. java集成网站微信,微博,qq登录

    微信 WechatConfig.java package com.meeno.chemical.common.sdk.wechat.config; import org.springframework ...

  10. 4、ABPZero系列教程之拼多多卖家工具 集成短信发送模块

    ABPZero并没有手机短信发送功能,现在我们来集成一个,为后面注册.登录作铺垫. 阿里云短信服务 首先需要在阿里云开通短信服务,连接地址 开通后,在签名管理中添加一个签名 在模板管理中添加一个模板, ...

随机推荐

  1. 【pygame】Python小游戏开发之看代码学编程

    话说我学习的时候,英文文档难以理解,中文文档杂乱无章,最终还是觉得,还不如直接看代码学习. 下面是我学习过程中写的代码,注释写的很详细,我想会帮助你理解的 pip install pygame 1.游 ...

  2. Qt音视频开发38-ffmpeg视频暂停录制的设计

    一.前言 基本上各种播放器提供的录制视频接口,都是只有开始录制和结束录制两个,当然一般用的最多的也是这两个接口,但是实际使用过程中,还有一种可能需要中途暂停录制,暂停以后再次继续录制,将中间部分视频不 ...

  3. FFmpeg中的色彩空间与像素格式2-RGB/YUV色彩空间

    cnblogs 网站将文本J:a:b渲染成了J️b.是否可通过设置博客后台解决此问题?有知道的同学请留言指点一下,谢谢. FFmpeg 中的色彩与像素系列文章如下: [1]. FFmpeg中的色彩空间 ...

  4. [转]swing中如何将jtable中的数据导入到excel中?

    这个版本的代码是可以支持中文,需要导入jxl.jar包,并添加到Build Path中(自行搜索下载). 最终代码: package test; import java.awt.event.*; im ...

  5. JVM实战—6.频繁YGC和频繁FGC的后果

    大纲 1.JVM GC导致系统突然卡死无法访问 2.什么是Young GC什么是Full GC 3.Young GC.Old GC和Full GC的发生情况 4.频繁YGC的案例(G1解决大内存YGC ...

  6. Event-Stream技术

    服务端 websocket和event-stream的优缺点 WebSocket和Event-Stream(Server-Sent Events)都是实现实时通信的技术,但是它们各自有不同的优缺点. ...

  7. w3cschool-Python3 教程

    https://www.w3cschool.cn/python3/ Python 特点 1.易于学习:Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单. 2.易于阅读: ...

  8. C# 单例简单实例

    1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Li ...

  9. Java怎样实现将数据导出为Word文档

    文章首发于我的博客:Java怎样实现将数据导出为Word文档 - Liu Zijian's Blog 我们在开发一些系统的时候,例如OA系统,经常能遇到将审批单数据导出为word和excel文档的需求 ...

  10. Billyboss pg walkthough Intermediate window

    nmap ┌──(root㉿kali)-[/home/ftpuserr/nc.exe] └─# nmap -p- -A -sS 192.168.219.61 Starting Nmap 7.94SVN ...