ServiceStack - 电子邮件确认
ServiceStack - Email Confirmation
我正在尝试在 ServiceStack 中实现电子邮件确认。我花了 5 天时间试图理解所有的身份验证机制,我必须说它非常复杂。代码不易阅读。
我很难知道例如:
- 为什么叫
IAuthProvider.OnAuthenticated
?有时它似乎是可选的。
- 为什么叫
IAuthSession.PopulateSession
?它与 IAuthProvider.PopulateSession
有何不同?
- 为什么叫
IRequest.SaveSession
?
- 什么是
IAuthTokens
?为什么它们在某个时候合并?是不是因为一个用户可以通过很多不同的提供者进行认证,我们需要合并IAuthTokens
?他们喜欢索赔吗?
Session.FromToken
仅适用于 JWT 吗?
IAuthWithRequest.PreAuthenticate
和 IAuthProvider.Authenticate
有何不同?在这两种方法中应该有什么不同的做法?我收集到每个请求都会调用 PreAuthenticate,并且在使用 /auth/{provider} 时会调用 Authenticate(在本例中为 /auth/email)。我错过了什么吗?
抛开这些问题,为了解决问题,我尝试了两种方法:
实施 EmailConfirmationAuthProvider
通过添加 bool EmailConfirmed {get;set;}
字段将默认 UserAuth
替换为 CustomUserAuth
。
当用户注册时,会向用户发送一封带有秘密令牌的电子邮件。此令牌存储在某处。
当用户点击 link (https://blabla.com/auth/email?token=abcdef) 时,如果令牌有效,EmailConfirmationAuthProvider.PreAuthenticate
会将 CustomUserAuth.EmailConfirmed
标记为 true并且没有过期。令牌已删除,因此无法使用该令牌再次进行身份验证。
我不知道如何将 EmailConfirmed = true
添加到会话中,也不知道如何在后续登录时添加它。而且我不知道如何不妨碍其他身份验证机制。
实施 EmailConfirmationService
(步骤同1.1和1.2)
当用户点击 link (https://blabla.com/email-confirmation?token=abcdef) 时,如果令牌有效,EmailConfirmationService
会将 CustomUserAuth.EmailConfirmed
标记为 true并且没有过期。令牌已删除,无法重复使用相同的令牌。
我不知道如何从 CustomUserAuth
重新填充会话,特别是如果用户已经使用 JWT 令牌进行身份验证(如果 JwtAuthProvider
是AuthFeature.AuthProviders
的一部分)。我想找到一种方法来为该用户的所有后续登录向会话添加 EmailConfirmed 标志。我没有找到如何 "modify" JWT 令牌(通过添加 EmailConfirmed = true),并将其作为 cookie 发回,这样就不必在每次请求时都检查电子邮件是否得到确认。
基本上,我想将所有标有 [Authenticate]
的服务的访问权限限制为已确认其电子邮件地址的用户。如果我错了,请纠正我,但这应该只适用于使用凭据注册的用户(如果用户使用 GoogleAuthProvider
登录,则电子邮件确认不应适用?或者应该吗?)
此规则的例外情况是某些服务(标有 [AllowUnconfirmedEmail]
属性)允许用户向其地址重新发送确认电子邮件(假设他们输入了错误的电子邮件地址) ). EmailConfirmationService
可以允许经过身份验证的用户向新地址发送新确认。单击该确认电子邮件后,CustomUserAuth.Email & CustomUserAuth.PrimaryEmail
将更新为新地址。
请参考ServiceStack Authentication Docs to explain how session based Auth Providers and IAuthWithRequest
Auth Providers作品中的high-level图。
其中很多问题都是内部实现细节,当不同的 Auth Provider 需要时会调用它们。您可以添加自定义逻辑的用户可覆盖事件发布在 Session and Auth Events.
PreAuthenticate()
由 IAuthWithRequest
Auth Providers that authenticate per-request like API Key and JWT 授权提供商调用。 Authenticate()
在通过 /auth
提供商进行身份验证时被调用。
正在实施电子邮件确认
实现用户需要确认电子邮件的最简单方法可能是实现 Custom UserSession Validation 类似的东西:
public class CustomUserSession : AuthUserSession
{
public override IHttpResult Validate(IServiceBase authService, IAuthSession session,
IAuthTokens tokens, Dictionary<string, string> authInfo)
{
using (var db = HostContext.AppHost.GetDbConnection(authService.Request))
{
var userAuthId = int.Parse(session.UserAuthId);
if (!db.Exists<CustomUserAuth>(x => x.Id == userAuthId && x.EmailConfirmed))
return HttpError.Conflict($"Email not confirmed") as IHttpResult;
}
return null;
}
}
所以只有当用户有一个确认的电子邮件地址时它才允许身份验证。
请参阅 Extending UserAuth Tables 了解如何将自定义 UserAuth
table 与 EmailConfirmed
.
等其他元数据一起使用
您的电子邮件服务将是一个接受随机字符串的服务,例如 Guid
(在 CustomUserAuth
或单独的 table 中),引用哪个用户已确认他们的电子邮件.或者,电子邮件中的 link 可以再次包含该电子邮件,在这种情况下,您可以将其与 CustomUserAuth
table.[=] 中 Email
字段中的现有电子邮件进行匹配40=]
如果您随后想在同一个请求中对用户进行身份验证,您可以通过配置 CredentialsAuthProvider
来允许 password-less Authenticated requests:
new CredentialsAuthProvider {
SkipPasswordVerificationForInProcessRequests = true,
}
这将让您在同一 "in process" 请求中仅使用用户名进行身份验证:
using (var service = base.ResolveService<AuthenticateService>()) //In Process
{
return service.Post(new Authenticate {
provider = AuthenticateService.CredentialsProvider,
UserName = request.UserName,
UseTokenCookie = true, // if using JWT
});
}
您可以使用 UseTokenCookie to authenticate the User with a HTTP Only JWT Session,但不需要使用 JWT。
此解决方案不需要它,但您可以通过 implementing CreatePayloadFilter 向 JWT 令牌添加额外信息,并通过实施 PopulateSessionFilter
.[=40= 在用户会话中填充额外元数据]
I don't know how to add the EmailConfirmed = true to the session
您可以通过在自定义 AuthUserSession
上实施 OnAuthenticated()
来填充 UserSession。
Basically, I want to restrict access to all services marked with [Authenticate] to users that have confirmed their email addresses.
如上所述在您的自定义 AuthUserSession 中覆盖 Validate()
将确保只有拥有已确认电子邮件的用户才能进行身份验证。
Exception to this rule would be for some services
没有 "partially authenticated user" 的概念,您要么已通过身份验证(并且已将经过身份验证的 UserSession 附加到请求或缓存),要么未通过身份验证。如果他们确认了他们的电子邮件,我只会允许身份验证,并且只有在他们已经确认他们想要将其更改为建议的电子邮件时,我才会更新他们确认的电子邮件。
我正在尝试在 ServiceStack 中实现电子邮件确认。我花了 5 天时间试图理解所有的身份验证机制,我必须说它非常复杂。代码不易阅读。
我很难知道例如:
- 为什么叫
IAuthProvider.OnAuthenticated
?有时它似乎是可选的。 - 为什么叫
IAuthSession.PopulateSession
?它与IAuthProvider.PopulateSession
有何不同? - 为什么叫
IRequest.SaveSession
? - 什么是
IAuthTokens
?为什么它们在某个时候合并?是不是因为一个用户可以通过很多不同的提供者进行认证,我们需要合并IAuthTokens
?他们喜欢索赔吗? Session.FromToken
仅适用于 JWT 吗?IAuthWithRequest.PreAuthenticate
和IAuthProvider.Authenticate
有何不同?在这两种方法中应该有什么不同的做法?我收集到每个请求都会调用 PreAuthenticate,并且在使用 /auth/{provider} 时会调用 Authenticate(在本例中为 /auth/email)。我错过了什么吗?
抛开这些问题,为了解决问题,我尝试了两种方法:
实施 EmailConfirmationAuthProvider
通过添加
bool EmailConfirmed {get;set;}
字段将默认UserAuth
替换为CustomUserAuth
。当用户注册时,会向用户发送一封带有秘密令牌的电子邮件。此令牌存储在某处。
当用户点击 link (https://blabla.com/auth/email?token=abcdef) 时,如果令牌有效,
EmailConfirmationAuthProvider.PreAuthenticate
会将CustomUserAuth.EmailConfirmed
标记为 true并且没有过期。令牌已删除,因此无法使用该令牌再次进行身份验证。我不知道如何将
EmailConfirmed = true
添加到会话中,也不知道如何在后续登录时添加它。而且我不知道如何不妨碍其他身份验证机制。
实施
EmailConfirmationService
(步骤同1.1和1.2)
当用户点击 link (https://blabla.com/email-confirmation?token=abcdef) 时,如果令牌有效,
EmailConfirmationService
会将CustomUserAuth.EmailConfirmed
标记为 true并且没有过期。令牌已删除,无法重复使用相同的令牌。我不知道如何从
CustomUserAuth
重新填充会话,特别是如果用户已经使用 JWT 令牌进行身份验证(如果JwtAuthProvider
是AuthFeature.AuthProviders
的一部分)。我想找到一种方法来为该用户的所有后续登录向会话添加 EmailConfirmed 标志。我没有找到如何 "modify" JWT 令牌(通过添加 EmailConfirmed = true),并将其作为 cookie 发回,这样就不必在每次请求时都检查电子邮件是否得到确认。
基本上,我想将所有标有 [Authenticate]
的服务的访问权限限制为已确认其电子邮件地址的用户。如果我错了,请纠正我,但这应该只适用于使用凭据注册的用户(如果用户使用 GoogleAuthProvider
登录,则电子邮件确认不应适用?或者应该吗?)
此规则的例外情况是某些服务(标有 [AllowUnconfirmedEmail]
属性)允许用户向其地址重新发送确认电子邮件(假设他们输入了错误的电子邮件地址) ). EmailConfirmationService
可以允许经过身份验证的用户向新地址发送新确认。单击该确认电子邮件后,CustomUserAuth.Email & CustomUserAuth.PrimaryEmail
将更新为新地址。
请参考ServiceStack Authentication Docs to explain how session based Auth Providers and IAuthWithRequest
Auth Providers作品中的high-level图。
其中很多问题都是内部实现细节,当不同的 Auth Provider 需要时会调用它们。您可以添加自定义逻辑的用户可覆盖事件发布在 Session and Auth Events.
PreAuthenticate()
由 IAuthWithRequest
Auth Providers that authenticate per-request like API Key and JWT 授权提供商调用。 Authenticate()
在通过 /auth
提供商进行身份验证时被调用。
正在实施电子邮件确认
实现用户需要确认电子邮件的最简单方法可能是实现 Custom UserSession Validation 类似的东西:
public class CustomUserSession : AuthUserSession
{
public override IHttpResult Validate(IServiceBase authService, IAuthSession session,
IAuthTokens tokens, Dictionary<string, string> authInfo)
{
using (var db = HostContext.AppHost.GetDbConnection(authService.Request))
{
var userAuthId = int.Parse(session.UserAuthId);
if (!db.Exists<CustomUserAuth>(x => x.Id == userAuthId && x.EmailConfirmed))
return HttpError.Conflict($"Email not confirmed") as IHttpResult;
}
return null;
}
}
所以只有当用户有一个确认的电子邮件地址时它才允许身份验证。
请参阅 Extending UserAuth Tables 了解如何将自定义 UserAuth
table 与 EmailConfirmed
.
您的电子邮件服务将是一个接受随机字符串的服务,例如 Guid
(在 CustomUserAuth
或单独的 table 中),引用哪个用户已确认他们的电子邮件.或者,电子邮件中的 link 可以再次包含该电子邮件,在这种情况下,您可以将其与 CustomUserAuth
table.[=] 中 Email
字段中的现有电子邮件进行匹配40=]
如果您随后想在同一个请求中对用户进行身份验证,您可以通过配置 CredentialsAuthProvider
来允许 password-less Authenticated requests:
new CredentialsAuthProvider {
SkipPasswordVerificationForInProcessRequests = true,
}
这将让您在同一 "in process" 请求中仅使用用户名进行身份验证:
using (var service = base.ResolveService<AuthenticateService>()) //In Process
{
return service.Post(new Authenticate {
provider = AuthenticateService.CredentialsProvider,
UserName = request.UserName,
UseTokenCookie = true, // if using JWT
});
}
您可以使用 UseTokenCookie to authenticate the User with a HTTP Only JWT Session,但不需要使用 JWT。
此解决方案不需要它,但您可以通过 implementing CreatePayloadFilter 向 JWT 令牌添加额外信息,并通过实施 PopulateSessionFilter
.[=40= 在用户会话中填充额外元数据]
I don't know how to add the EmailConfirmed = true to the session
您可以通过在自定义 AuthUserSession
上实施 OnAuthenticated()
来填充 UserSession。
Basically, I want to restrict access to all services marked with [Authenticate] to users that have confirmed their email addresses.
如上所述在您的自定义 AuthUserSession 中覆盖 Validate()
将确保只有拥有已确认电子邮件的用户才能进行身份验证。
Exception to this rule would be for some services
没有 "partially authenticated user" 的概念,您要么已通过身份验证(并且已将经过身份验证的 UserSession 附加到请求或缓存),要么未通过身份验证。如果他们确认了他们的电子邮件,我只会允许身份验证,并且只有在他们已经确认他们想要将其更改为建议的电子邮件时,我才会更新他们确认的电子邮件。