REST API上的用户注册/身份验证流程

时间:2015-01-10 12:37:55

标签: api rest authentication web-applications asp.net-web-api

我知道这不是第一次在StackOverflow中处理该主题,但是,我有一些问题我无法找到答案或其他问题反对答案。

我正在做一个相当简单的REST API(Silex-PHP),最初仅由一个SPA(骨干应用程序)使用。我不想在这个问题中评论所有几种身份验证方法,因为该主题已经在SO上完全涵盖。我基本上会为每个用户创建一个令牌,这个令牌将附加在需要SPA验证的每个请求中。所有SPA-Server事务都将在HTTPS下运行。目前,我的决定是令牌不会过期。每个会话过期/令牌的令牌不符合REST的无状态,对吧?我知道有很大的安全改进空间,但这是我现在的范围。

我有一个Tokens模型,因此数据库中有一个表,其中包含一个FK到user_id的标记。我的意思是令牌不是我的用户模型的一部分。

REGISTER

我有一个POST / users(不需要身份验证),可以在数据库中创建用户并返回新用户。这符合一个请求一个资源规则。然而,这给我带来了一些疑问:

  • 我的想法是,在创建新用户时,为用户创建一个新令牌,立即将其与Response一起返回,从而改善用户体验。用户将立即开始使用Web应用程序。但是,返回令牌以进行此类响应会破坏仅返回资源的规则。我应该一起提出两个请求吗?一个用于创建用户,另一个用于检索令牌而无需用户重新输入凭据?

  • 如果我决定将令牌与用户一起返回,那么我相信POST /用户会对API使用者感到困惑,然后出现类似POST / auth / register的内容。再一次,我不喜欢这个想法因为涉及一个动词。我非常喜欢this answer中提供的简洁性。但话说回来,我需要一起做两个请求,POST / users和POST / tokens。一起做两个请求有多么错,而且,如果两个请求一起发送,我将如何准确地发送附加到某个用户的令牌的相关信息?

现在我的流程如下:

1. Register form makes a POST /users request
2. Server creates a new user and a new token, returns both in the response (break REST rule)
3. Client now attaches token to every Request that needs Authorization

令牌永不过期,保留REST无状态。

电子邮件验证

大多数当前的Web应用程序都需要电子邮件验证,而不会破坏用户的用户体验,即用户可以在注册后立即使用webapp。另一方面,如果我按照上面的建议返回带有注册请求的令牌,用户将立即访问每个资源而无需验证电子邮件。

通常我会选择以下工作流程:

1. Register form sends POST /users request.
2. Server creates a new user with validated_email set to false and stores an email_validation_token. Additionally, the server sends an email generating an URL that contains the email_validation_token. 
3. The user clicks on the URL that makes a request: For example POST /users/email_validation/{email_validation_token}
4. Server validates email, sets validated_email to true, generates a token and returns it in the response, redirecting the user to his home page at the same time.

这看起来过于复杂,完全破坏了用户体验。你怎么样的?

LOGIN

这很简单,现在我这样做,所以如果错误请纠正我:

1. User fills a log in form which makes a request to POST /login sending Basic Auth credentials.
2. Server checks Basic Auth credentials and returns token for the given user.
3. Web app attached the given token to every future request.

登录是动词,因此打破了REST规则,但每个人似乎都同意这样做。

LOGOUT

为什么每个人似乎都需要/ auth / logout端点?从我的观点来看,点击Web应用程序中的“注销”基本上应该从应用程序中删除令牌,而不是在进一步的请求中发送它。服务器在这方面没有任何作用。

由于令牌可能保留在localStorage中以防止在可能的页面刷新时丢失令牌,因此注销也意味着从localStorage中删除令牌。但是,这仍然不会影响服务器。我理解需要POST /注销的人基本上使用会话令牌,这再次打破了REST的无状态。

记住我

我理解记住我基本上是指在我的情况下将返回的令牌保存到localStorage。这是对的吗?

如果您对此主题有任何进一步的阅读建议,我将非常感激。谢谢!

3 个答案:

答案 0 :(得分:10)

<强> REGISTER

  

每个会话过期/令牌的令牌不符合REST的无状态,对吧?

不,那没有错。许多HTTP身份验证方案都有到期令牌。 OAuth2在REST服务中非常流行,许多OAuth2实现都会强制客户端不时刷新访问令牌。

  

我的想法是,在创建新用户时,为用户创建一个新令牌,立即使用Response返回它,从而改善用户体验。用户将立即开始使用Web应用程序。但是,返回令牌以进行此类响应会破坏仅返回资源的规则。我应该一起提出两个请求吗?一个用于创建用户,一个用于检索令牌而无需用户重新输入凭据?

通常,如果您按照REST最佳做法创建新资源,则不会返回响应此类POST的内容。这样做会使调用更像RPC,所以我在这里同意你的看法......它并不完全是RESTful。我将为此提供两种解决方案:

  1. 忽略这一点,打破最佳做法。也许它在这种情况下是最好的,如果它们更有意义的话,做出例外有时是最好的事情(仔细考虑之后)。
  2. 如果你想要更加RESTful,我会提供另一种选择。
  3. 让我们假设你想使用OAuth2(不是个坏主意!)。由于多种原因,OAuth2 API实际上并不是RESTful。我的想法是,最好使用定义良好的身份验证API,而不是为了RESTful而自己滚动。

    这仍然会让您在API上创建用户,并在响应此(POST)调用时返回一个可用作访问/刷新令牌的秘密。

    我的替代方案如下:

    您不需要有用户才能开始会话

    您可以做的是在创建用户之前启动会话。这保证了对于将来的通话,您知道您正在与同一客户通话。

    如果您启动OAuth2流程并收到访问/刷新令牌,则只需在/users上执行经过身份验证的POST请求即可。这意味着您的系统需要了解两种经过身份验证的用户:

    1. 使用用户名/密码登录的用户(`grant_type = passsword1)。
    2. 以匿名方式登录的用户&#39;并打算在事后创建一个用户。 (grant_type = client_credentials)。
    3. 创建用户后,您可以使用新创建的用户实体分配以前的匿名会话,因此您无需在创建后进行任何访问/刷新令牌交换。

      电子邮件验证

      你的建议都是:

      • 在完成电子邮件验证之前,阻止用户使用该应用程序。
      • 允许用户立即使用该应用程序

      由应用程序完成。哪一种更合适,取决于您的应用以及最适合您的应用。是否有任何风险与用户开始使用他们不拥有的电子邮件的帐户相关联?如果不是,那么允许用户马上就可以了。

      以下是您不想这样做的示例:假设您的系统的其他成员使用该电子邮件地址将用户添加为朋友,则电子邮件地址是一种类型身份。如果您不强制用户验证其电子邮件,则表示我可以代表具有不同电子邮件地址的用户行事。这类似于能够接收邀请等。这是一个攻击媒介吗?然后,您可能需要考虑阻止用户使用该应用程序,直到验证电子邮件。

      您可能还会考虑仅阻止应用中某些电子邮件地址可能敏感的功能。在上一个示例中,您可以阻止人们在验证电子邮件之前看到其他用户的邀请。

      这里没有正确答案,这取决于您打算如何使用电子邮件地址。

      <强> LOGIN

      请使用OAuth2。您描述的流程已经非常接近OAuth2的工作方式。更进一步,实际上使用OAuth2。它非常棒,一旦你克服了理解协议的最初障碍,你会发现它比你想象的要容易,并且只是简单地实现你特别需要的API

      大多数PHP OAuth2服务器实现都不是很好。它们做得太多,有点难以整合。滚动自己并不那么难,你已经非常接近于建立类似的东西。

      <强> LOGOUT

      您可能需要注销终结点的两个原因是:

      1. 如果您使用基于cookie /会话的身份验证,并希望告诉服务器忘记会话。听起来这对你来说不是问题。
      2. 如果您想告诉服务器提前使访问/刷新令牌过期。是的,你可以从localstorage中删除它们,这可能就足够了。强制它们在服务器端到期可能会给你一点额外的信心。如果有人能够MITM您的浏览器并且现在可以访问您的令牌怎么办?我可能希望快速注销并使所有现有令牌过期。这是一个边缘案例,我个人从来没有这样做,但可能是你想要它的原因。
      3. 记住我

        是的,实施&#34;记住我&#34;使用本地存储听起来是个好主意。

答案 1 :(得分:0)

我最初采用/LOGON/LOGOUT方法。我开始探索/PRESENCE。看来这有助于我将知道某人的身份和身份验证结合在一起。

0 = Offline
1 = Available
2 = Busy

Offline转到其他任何内容都应包括初始验证(也需要用户名/密码)。您可以为此使用PATCHPUT(取决于您的观看方式)。

答案 2 :(得分:-1)

你是对的,REST不允许使用SESSION,因此无需登录或注销REST服务和/ login,/ logout不是名词。

对于身份验证,您可以使用

  1. 基于SSL的基本身份验证
  2. 摘要式身份验证
  3. OAuth 2
  4. HMAC等
  5. 我更喜欢使用PUBLIC KEY和PRIVATE KEY [HMAC]

    私钥永远不会通过网络传输,我不关心公钥。公钥将用于进行用户特定的操作[谁持有api密钥]

    客户端应用和服务器将知道私钥。私钥将用于创建签名。您使用私钥生成签名令牌,并将密钥添加到标头中。服务器还将生成签名并验证握手请求。

    Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
    

    现在你将如何获得私钥?你必须手动完成,就像你在你的应用程序上放置Facebook,Twitter或谷歌API密钥。

    但是,在某些情况下,您也可以像亚马逊S3一样只返回[不推荐]密钥一次。他们在注册响应时提供“AWS秘密访问密钥”。