.net8 C#集成 AWS Cognito SMS/Email 注册登录

打印 上一主题 下一主题

主题 974|帖子 974|积分 2922

本文重要分为三个部分:
1、描述 cognito 涉及的专业术语 以及 交互流程
2、.net 集成的代码
3、感想
* 阅读提示 :鼠标悬停在 章节标题 上可见 文章目录
 
 
1. Cognito 概念

1.1 关键词

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

 
cognito亚马逊(Amazon)云 提供的一种用户管理服务,简化用户注册、登录和授权、鉴权相关的服务操纵。可以在 cognito 上创建用户池,管理用户注册、登录和鉴权相关的问题;可以创建 identity pool 对用户进行授权相关的束缚;支持多种身份验证方式,比方 用户 / 暗码 登录,交际帐号( Facebook,Google 等)登录,企业身份供应商 * What is Amazon Cognito? - Amazon Cognito
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 challenge,create auth challenge,verify 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 Response
custom authentication 的第三步,校验用户输入的答案与在 create auth challenge 中预设的答案是否划一
 
下文将描述我们的代码会如何与 cognito 交互,又是如何触发对应的 lambda function。
 
 
1.2. 流程图

登录步骤 1:输入账号



  • 前端第一次调用接口,是提交登录的哀求;
  • 后端调用 AWSSDK.CognitoIdentifyProvider 中 cognitoClient.InitiateAuthAsync 方法,这个方法是 cognito 支持的自定义验证方式,它会自动触发到 define auth challenge,create 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 上,它进一步定义验证的流程
比方,用户通过暗码验证之后,将进入得手机短信验证
  1. // cognito 将会触发到配置在 user pool 上的 lambda trigger,event 是它的入参
  2. exports.handler = async (event) => {
  3.     if (event.request.session.length === 1 && event.request.session[0].challengeName === 'SRP_A') {
  4.      // 是否直接发布 token
  5.         event.response.issueTokens = false;
  6.         // 是否验证失败
  7.         event.response.failAuthentication = false;
  8.         // 下一个验证方式的名字
  9.         event.response.challengeName = 'PASSWORD_VERIFIER';
  10.     } else if (event.request.session.length === 2 && event.request.session[1].challengeName === 'PASSWORD_VERIFIER' && event.request.session[1].challengeResult === true) {
  11.         event.response.issueTokens = false;
  12.         event.response.failAuthentication = false;
  13.         event.response.challengeName = 'CUSTOM_CHALLENGE';
  14.     } else if (event.request.session.length === 3 && event.request.session[2].challengeName === 'CUSTOM_CHALLENGE' && event.request.session[2].challengeResult === true) {
  15.         event.response.issueTokens = true;
  16.         event.response.failAuthentication = false;
  17.     } else {
  18.         event.response.issueTokens = false;
  19.         event.response.failAuthentication = true;
  20.     }
  21.     return event;
  22. };
复制代码
 
 
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 实现示例:
  1.    exports.handler = async (event) => {
  2.        if (event.request.challengeName === 'CUSTOM_CHALLENGE') {
  3.            const otp = '654321'; // Generate or set your OTP here
  4.            event.response.publicChallengeParameters = { otp: 'Enter the OTP sent to your email' };
  5.            event.response.privateChallengeParameters = { otp: otp };
  6.            event.response.challengeMetadata = 'CUSTOM_CHALLENGE';
  7.        }
  8.        return event;
  9.    };
复制代码
 
 
Verify auth challenge


  • 该触发器在用户提交验证码之后被触发
  • 该触发器接收到用户输入的 验证码 后会与 Create Auth Challenge 中的验证码相互匹配。如果校验正确,cognito 会处理返回的 event 并生成 token
lambda function 实现示例:
  1.    // TriggerSource is VerifyAuthChallengeResponse_Authentication
  2.    exports.handler = async (event) => {
  3.        const expectedOtp = event.request.privateChallengeParameters.otp;
  4.        const userResponse = event.request.challengeAnswer;
  5.        event.response.answerCorrect = userResponse === expectedOtp;
  6.        return event;
  7.    };
复制代码
 
 
 
 
2. 集成到 .net 8 api

.NET 中需要使用 AWS SDK 提供的 API 来与 Cognito 进行交互,
引用包:AWSSDK.CognitoIdentityProvider ,下文代码使用的版本是:
  1. using Amazon.Extensions.CognitoAuthentication;
  2. using Amazon.CognitoIdentityProvider;
  3. using Amazon.CognitoIdentityProvider.Model;
复制代码
 
cognitoClient 初始化: 
 
 
2.1.1 注册 [signup]
  1.         public async Task<SignUpResponse> SignupAsync(SignUpDto user)
  2.         {
  3.             var userName = string.IsNullOrEmpty(user.Email) ? user.PhoneNumber : user.Email;
  4.             var request = new SignUpRequest
  5.             {
  6.                 // 在 cognito user pool 中配置允许访问该 user pool 的 client 后,可以在cognito 上查看到这个 clientId
  7.                 ClientId = _clientId,
  8.                
  9.                 // cognito 会校验代码复杂度,需要在 user pool 中配置;但此示例中密码是没有作用的
  10.                 Password = "carcar@2024",
  11.                
  12.                 // 此处可以填手机号或邮箱地址; cognito user pool 中显示的 userName 是基于 user pool 上的 uuid
  13.                 Username = userName
  14.             };
  15.         
  16.             // 自定义的 user attribute
  17.             var nameAttribute = new AttributeType
  18.             {
  19.                 Name = "name",
  20.                 Value = user.Name
  21.             };
  22.             request.UserAttributes.Add(nameAttribute);
  23.             // 自定义的 user attribute
  24.             var emailAttribute = new AttributeType
  25.             {
  26.                 Name = "email",
  27.                 Value = user.Email
  28.             };
  29.             request.UserAttributes.Add(emailAttribute);
  30.             return await _cognitoClient.SignUpAsync(request);
  31.         }
复制代码
 
查看暗码复杂度设置
 
 
查看 user attribute

 
 
2.1.2 注册验证 [signup-confirm]
  1. var confirmSignUpRequest = new ConfirmSignUpRequest
  2. {
  3.      ClientId = "client id",
  4.      Username = "user phone / email",
  5.      ConfirmationCode = "verification code from sms or email"
  6. };
  7. var confirmSignUpResponse = await cognitoClient.ConfirmSignUpAsync(confirmSignUpRequest);
  8. Console.WriteLine($"User {confirmSignUpResponse.UserConfirmed} confirmed successfully.")
复制代码
 
2.1.3 重新发送验证码 [resend-confirmation]
  1. public async Task ResendConfirmationAsync(string cognitoUserName)
  2. {
  3.     var request = new ResendConfirmationCodeRequest
  4.     {
  5.         ClientId = _clientId,
  6.         Username = cognitoUserName
  7.     };
  8.     var response = await _cognitoClient.ResendConfirmationCodeAsync(request);
  9. }
复制代码
 
2.2.1 登录 [signin]
  1. var authRequest = new InitiateAuthRequest
  2. {
  3.      ClientId = "the client id which configures in the user pool",
  4.      AuthFlow = AuthFlowType.CUSTOM_AUTH,
  5.      AuthParameters = new Dictionary<string, string>
  6.      {
  7.          { "USERNAME", "your_username_or_phone_or_email" }
  8.      }
  9. };
  10. var authResponse = await cognitoClient.InitiateAuthAsync(authRequest);
复制代码
 
 
2.2.2 登录验证 [signin-confirm]

校验验证码时,调用 RespondToAuthChallengeAsync
  1. var respondToAuthChallengeRequest = new RespondToAuthChallengeRequest
  2. {
  3.      ChallengeName = authResponse.ChallengeName,
  4.      ClientId = "your_client_id",
  5.      ChallengeResponses = new Dictionary<string, string>
  6.      {
  7.          { "USERNAME", "your_username_or_phone_or_email" },
  8.          { "SMS_MFA_CODE", "user_received_code" }
  9.      },
  10.      Session = authResponse.Session
  11. };
  12. var challengeResponse = await cognitoClient.RespondToAuthChallengeAsync(respondToAuthChallengeRequest);
  13. var idToken = challengeResponse.AuthenticationResult.IdToken;
复制代码
 
 
2.3 token 验证

在 Program 或 StartUp 的设置文件中,需要设置 Authentication 中间件
* token 参数阐明点此跳转
  1. builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  2.     .AddJwtBearer(options =>
  3.     {
  4.         options.TokenValidationParameters = new TokenValidationParameters
  5.         {
  6.             ValidateIssuer = true,
  7.             ValidateAudience = false,
  8.             ValidateLifetime = true,
  9.             ValidateIssuerSigningKey = true,
  10.             ValidIssuer = $"https://cognito-idp.{AWSregion}.amazonaws.com/{userPoolId}",
  11.             // ValidAudience = {userPoolId},
  12.             IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
  13.             {
  14.                 // Get JsonWebKeySet from AWS
  15.                 var json = new WebClient().DownloadString($"https://cognito-idp.{AWSregion}.amazonaws.com/{userPoolId}/.well-known/jwks.json");
  16.                 // Deserialize the result
  17.                 var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
  18.                 // Cast the result to be the type expected by IssuerSigningKeyResolver
  19.                 return (IEnumerable<SecurityKey>)keys;
  20.             }
  21.         };
  22.     });
复制代码
 
 
 
 
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
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

我爱普洱茶

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表