本文重要分为三个部分:
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 上,它进一步定义验证的流程
比方,用户通过暗码验证之后,将进入得手机短信验证- // 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 ,下文代码使用的版本是:- using Amazon.Extensions.CognitoAuthentication;
- using Amazon.CognitoIdentityProvider;
- using Amazon.CognitoIdentityProvider.Model;
复制代码
cognitoClient 初始化:
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
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]
- public async Task ResendConfirmationAsync(string cognitoUserName)
- {
- var request = new ResendConfirmationCodeRequest
- {
- ClientId = _clientId,
- Username = cognitoUserName
- };
- 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
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |