OAuth 2授权代码授予实施

时间:2020-05-22 14:14:19

标签: php vue.js authentication oauth-2.0 authorization

我有以下设置:

资源服务器:这是一个用PHP(Slim,Doctrine)编写的基本RESTful API

Auth Server:我自己在PHP(Slim,Doctrine)中实现的OAuth 2服务器

公共客户端:用VueJS编写的SPA,可在网上公开获得。

我还想使用“授权码”或“客户端凭据”授予类型来授权更多客户端。

我已经阅读了大多数official OAuth 2 Framework规范以及其他一些实现该规范的“简单指南”和库。 我想我了解授权码授予类型流程的工作原理,但是在尝试实现它时,所有重定向都存在很多问题。 我知道有一个可行的解决方案,但是感觉很棘手,我相信必须有一个更简单,更好的解决方案。此外,由于我不是安全专家,所以我很担心 我的解决方案容易受到攻击。

因此,我的问题是:在不依赖许多第三方库的情况下,OAuth 2授权码授予类型的简单而安全的实现会是什么样子。

在这里,我将提供有关如何理解流程的图像,并将描述和提供解决方案的代码片段。

  • (第1步至第4步)用户单击网站上的登录按钮。触发了Vuex操作loginOAuth,该操作正在向Auth Server请求登录表单并将其显示在新窗口中。

Vuex操作loginOAuth

loginOAuth({commit}){
    let oauth_axios = axios.create({
      baseURL: '/php-own-oauth-server/API/public/api/v1/',
      timeout: 1000
    });
    oauth_axios({
      url: 'auth-form',
      method: 'GET'
    })
    .then(resp => {
      console.log(resp);

      let login_window = window.open('', 'My Name', 'left=0,top=0,width=800,height=500,toolbar=0,scrollbars=0,status=0');
      login_window.document.write(resp.data);
      login_window.document.close();
    })
    .catch(err => {
      commit('set_error_message', err);
    });
  },
  • (第5步至第7步)用户输入其用户凭据,然后单击表单上的Submit,这会将POST调用发送到Auth Server。如果凭据正确,服务器将响应 代码,在我的情况下是短暂的JWT。响应是通过window.opener.postMessage(xhr.responseText, '*');传递到主窗口的,在我看来这很hacky。我还没找到 做“使用代码重定向到应用程序”步骤的更好方法。为了对表示授权类型代码的JWT进行签名,我使用了一个简单的字符串密码,因为它只需要在 相同的身份验证服务器。

HTML登录表单,由OAuth服务器.../auth-form

返回
<head>
<script>
  window.onload = function() {
    var form = document.querySelector("form");
    form.onsubmit = submitted.bind(form);
  }

  function submitted(event) {
    event.preventDefault();
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "https://localhost/php-own-oauth-server/API/public/api/v1/auth-form-validate-user-credentials");
    xhr.onreadystatechange = function(event) {
      if (xhr.readyState == XMLHttpRequest.DONE) {
        if (xhr.status == 200) {
          window.opener.postMessage(xhr.responseText, '*');
          window.close();
        }
      }
    };
    var formData = new FormData(document.getElementById("myForm"));
    xhr.send(formData);
  }
</script>
</head>
<form id="myForm">
  <div class="form-group">
    <label for="email">Email address</label>
    <input type="email" class="form-control" id="email" name="email" aria-describedby="emailHelp">
  </div>
  <div class="form-group">
    <label for="password">Password</label>
    <input type="password" class="form-control" name="password" id="password">
  </div>
  <div class="form-group form-check">
    <input type="checkbox" class="form-check-input" id="exampleCheck1">
    <label class="form-check-label" for="exampleCheck1">Remember me</label>
  </div>
  <button type="submit" class="btn btn-primary" value="Submit">Login</button>
  <button class="btn btn-link">Password forgotten</button>
</form>

在VueJS应用程序中,我注册了以下事件侦听器,以接收登录表单发送的消息。 它从Auth Server接收响应,该响应主要是JWT令牌和 通过调度动作将其放回Vuex上下文中。

JavaScript事件侦听器可从登录表单中获得响应

window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
  if (event.data) {
    try {
      store.dispatch('Auth/requestAccessToken', JSON.parse(event.data));
    } catch (e) {

    }
  }
}
  • (第8步至第9步)该代码与client_id和client_secret(是的,我知道client_secret对于公共SPA客户端没有多大意义)一起使用,以从Auth Server请求访问令牌。 / li>

Vuex操作requestAccessToken

requestAccessToken({commit}, payload){
    let oauth_axios = axios.create({
      baseURL: '/php-own-oauth-server/API/public/api/v1/',
      timeout: 1000
    });
    oauth_axios({
      url: 'request-access-token',
      method: 'POST',
      data: {
        client_id: 'frontend_app',
        client_secret: 'test123!',
        code: payload.data.code
      }
    })
    .then(resp => {
      console.log(resp);
      // resp.data.data.access_token
    })
    .catch(err => {
      commit('set_error_message', err);
    });
  }

从这一点上来说,流程是直截了当的。我只是简单地将访问令牌作为标头添加到可以验证它的资源服务器的每个请求中。 该验证利用了私有和公共rsa密钥对。 Auth Server使用私钥对JWT进行签名,而Resource Server使用公钥对签名进行验证。

对我来说,这种解决方案感觉不对。是否有更好/更通用的解决方案来实施OAuth 2授权代码授予类型?我也对非Web应用程序客户端可以使用我的解决方案表示怀疑。

感谢您的阅读和帮助。 干杯

Raphael Hippe

1 个答案:

答案 0 :(得分:0)

OAuth架构应达到的一个好目标是通过以下步骤来实现安全性的外部化:

  • 使用来自AWS / Google / Microsoft的Cloud Authorization Server,对于中等程度的使用它可能是完全免费的
  • 使用SPA中基于标准的库,以减轻安全性并避免漏洞
  • 将精力集中在出色的UI和API上,并在升级以上各项时免费获得新的安全功能

My blog has some visual tutorials演示标准解决方案:

  • 如何将使用最广泛的库oidc-client集成到您的SPA中,并实施建议的授权码流(PKCE)
  • 如何与各种Authorization Server端点集成以及HTTPS消息有效负载的说明

与身份验证相关的代码在下面链接,并且如果实施得当,则不必编写太多代码: