本文主要分为三个部分:

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. logback高级特性使用(二) 自定义Pattern模板

    原文链接:https://blog.csdn.net/chenjie2000/article/details/8892764 创建自定义格式转换符有两步: 1.写一个转换器类,继承ClassicCon ...

  2. Qt音视频开发47-文字和图片水印(可存储到MP4中)

    一.前言 近期花了两周时间闭门啃硬骨头,主要就解决三个问题(音视频同步存储和推流.图片水印并将水印信息存储到文件或者推流.rtsp推流),这三个问题困扰了很多年,以至于找遍了网络和翻遍ffplay代码 ...

  3. Qt开发经验小技巧216-220

    Qt的网络库支持udp广播搜索和组播搜索,其中组播搜索可以跨网段搜索,有时候你会发现失灵,此时你可以尝试把本地的虚拟机的网卡禁用试试,估计就好了.还有就是在本地开启了代理啥的,先关掉试试.近期在使用t ...

  4. Qt编写可视化大屏电子看板系统29-模块7品质管理

    一.前言 品质管理模块是在送检合格模块的基础上增加了统计而来,总共包括了三个子模块:品质占比.班组合格率.每日合格率统计,其中品质占比子模块采用饼图控件显示对应的百分比,不同百分比颜色不一样,这个饼图 ...

  5. Datawhale冬令营第二期!Task2🌼

    Datawhale冬令营第二期-Task2:学AI编程的Prompt工程,提升效果 对应链接:https://www.datawhale.cn/activity/116/23/95?rankingPa ...

  6. .NET 9 new features-分布式追踪支持、HTTP/3 改进以及更好的容器镜像支持

    .NET 9 针对云原生开发进行了显著优化,重点改进了分布式追踪.HTTP/3 支持和容器镜像优化等方面. 这些特性极大地提升了 .NET 在现代云原生应用中的适配性与开发效率. 1. 设计原理 1. ...

  7. manim边做边学--动画组合

    动画组合类的作用是将多个动画组合起来,以实现更复杂的动画效果. Manim中有4个用于动画组合的类: AnimationGroup:将多个动画组合在一起同时播放,能一次性呈现多个对象的不同变化 Lag ...

  8. 自动化测试工具-Katalon Studio

              Katalon 代码片段: 1)if(WebUI.verifyTextPresent(findTestObject('Page_Skin/p_Are you ok?'),10,Fa ...

  9. 手把手带你使用Karpenter减少K8s集群资源浪费

    Kubernetes 集群的主要成本因素之一是数据平面上的计算层.将 Kubernetes 集群运行在 Amazon EC2 Spot 实例上是一种显著降低计算成本的有效方式.使用 Spot 实例可以 ...

  10. python基础学习4

    打开文件的方式 # 第一种 f = open('C:\project\pycharmprojects\\bigdata33\day05/cars.csv', mode='r', encoding='U ...