我有以下设置:
资源服务器:这是一个用PHP(Slim,Doctrine)编写的基本RESTful API
Auth Server:我自己在PHP(Slim,Doctrine)中实现的OAuth 2服务器
公共客户端:用VueJS编写的SPA,可在网上公开获得。
我还想使用“授权码”或“客户端凭据”授予类型来授权更多客户端。
我已经阅读了大多数official OAuth 2 Framework规范以及其他一些实现该规范的“简单指南”和库。 我想我了解授权码授予类型流程的工作原理,但是在尝试实现它时,所有重定向都存在很多问题。 我知道有一个可行的解决方案,但是感觉很棘手,我相信必须有一个更简单,更好的解决方案。此外,由于我不是安全专家,所以我很担心 我的解决方案容易受到攻击。
因此,我的问题是:在不依赖许多第三方库的情况下,OAuth 2授权码授予类型的简单而安全的实现会是什么样子。
在这里,我将提供有关如何理解流程的图像,并将描述和提供解决方案的代码片段。
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);
});
},
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) {
}
}
}
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
答案 0 :(得分:0)
OAuth架构应达到的一个好目标是通过以下步骤来实现安全性的外部化:
My blog has some visual tutorials演示标准解决方案:
与身份验证相关的代码在下面链接,并且如果实施得当,则不必编写太多代码: