Jan*_*ner 5 cookies firebase firebase-authentication google-cloud-functions firebase-admin
我正在关注此文档:管理会话Cookies
我的app.js看起来基本上是这样的:单击按钮即可在客户端登录用户。
(function() {
// Initialize Firebase
var config = {
//...
};
firebase.initializeApp(config);
// no local persistence because of the httpOnly flag
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
const emailField = document.getElementById("email");
const passwordField = document.getElementById("password");
const loginButton = document.getElementById("loginButton");
loginButton.addEventListener("click", e => {
const email = emailField.value;
const password = passwordField.value;
const signInPromise = firebase.auth().signInWithEmailAndPassword(email, password);
signInPromise.catch(e => {
console.log("Login Error: " + e.message);
})
return signInPromise.then(() => {
console.log("Signed in + " + firebase.auth().currentUser.uid);
return firebase.auth().currentUser.getIdToken().then(idToken => {
// Session login endpoint is queried and the session cookie is set.
// CSRF protection should be taken into account.
// ...
// const csrfToken = getCookie('csrfToken')
console.log("User ID Token: " + idToken);
return sendToken(idToken);
//return postIdTokenToSessionLogin('/sessionLogin', idToken, csrfToken);
});
})
});
firebase.auth().onAuthStateChanged(user => {
if (user) {
document.getElementById('loginSuccess').innerHTML = `Signed in as ${user.uid}`;
document.getElementById('loginError').innerHTML = "";
} else {
document.getElementById('loginSuccess').innerHTML = "";
document.getElementById('loginError').innerHTML = `Not signed in`;
}
});
})();
Run Code Online (Sandbox Code Playgroud)
sendToken函数如下所示:
function sendToken(idToken) {
console.log("Posting " + idToken);
var xhr = new XMLHttpRequest();
var params = `token=${idToken}`;
xhr.open('POST', "/admin/login", true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
return new Promise(function(resolve, reject) {
xhr.onreadystatechange = function() {//Call a function when the state changes.
if (xhr.readyState == 4 && xhr.status == 200) {
resolve();
} else if (xhr.readyState == 4 && xhr.status != 200) {
reject("Invalid http return status");
}
}
return xhr.send(params);
});
}
Run Code Online (Sandbox Code Playgroud)
在服务器端,我使用具有托管和firebase功能的Express应用程序。该/admin/login帖子看起来像这样:
adminApp.post("/login", (request, response) => {
console.log("Got login post request");
if (request.body.token) {
const idToken = request.body.token.toString();
console.log("idToken = " + idToken);
// Set session expiration to 5 days.
const expiresIn = 60 * 60 * 24 * 5 * 1000;
return adminFirebase.auth().createSessionCookie(idToken, {expiresIn}).then((sessionCookie) => {
const options = {maxAge: expiresIn, httpOnly: true, secure: true};
response.cookie('session', sessionCookie, options);
response.end(JSON.stringify({status: 'success'}));
}, error => {
response.status(401).send('UNAUTHORIZED REQUEST!');
});
}
return response.status(400).send("MISSING TOKEN");
});
Run Code Online (Sandbox Code Playgroud)
因此,发布后,sendToken我应该有一个名为“ session”的cookie,其中包含信息。所以现在我写了一些中间件来检查令牌:
const validateLogin = function (req, res, next) {
const sessionCookie = req.cookies.session || '';
console.log(JSON.stringify(req.headers));
console.log("Verifying " + sessionCookie);
return adminFirebase.auth().verifySessionCookie(sessionCookie, true).then((decodedClaims) => {
console.log("decoded claims: " + decodedClaims);
next();
}).catch(error => {
res.redirect('/admin/login');
});
};
Run Code Online (Sandbox Code Playgroud)
最后但并非最不重要的admin/secret一点是,我得到了使用此中间件的信息:
adminApp.get("/secret/", validateLogin, (request, response) => {
return response.send("This is secret!");
});
Run Code Online (Sandbox Code Playgroud)
但是,我经常被发送回登录页面。我缺少cookie才能正常工作吗?
我发现了火力地堡根据托管这个只允许一个饼干(否则会被剥离)。这个cookie是__session,但是设置这个cookie对我来说似乎也不起作用。
我能够在客户端设置__session cookie:
document.cookie = "__session=TOKEN"
Run Code Online (Sandbox Code Playgroud)
然后在服务器端验证令牌,但是cookie仅适用于本地/路径,不适用于本地路径/a/b
如果其他人正在访问此页面(就像我一个小时前所做的那样),这里是处理此问题的前端代码:
// Sign in with email and pass.
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => {
// Get the user's ID token and save it in the session cookie.
return firebase.auth().currentUser.getIdToken(true).then(function (token) {
// set the __session cookie
document.cookie = '__session=' + token + ';max-age=3600';
})
})
.catch(function (error) {//... code for error catching
Run Code Online (Sandbox Code Playgroud)
我希望它有帮助。
Him*_*Pal -2
@Janosch,这就是我设置应用程序的方式。我建议您浏览一下我关注的这个GitHub 存储库。
我的客户端是:
function signIn(){
var email = document.getElementById("username").value;
var password = document.getElementById("password").value;
// As httpOnly cookies are to be used, do not persist any state client side.
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
// When the user signs in with email and password.
firebase.auth().signInWithEmailAndPassword(email, password).then(user => {
// Get the user's ID token as it is needed to exchange for a session cookie.
return firebase.auth().currentUser.getIdToken().then(idToken => {
// Session login endpoint is queried and the session cookie is set.
// CSRF protection should be taken into account.
// ...
var csrfToken = getCookie('_csrf')
return postIdTokenToSessionLogin('/sessionLogin', idToken, csrfToken);
});
}).then(() => {
// A page redirect would suffice as the persistence is set to NONE.
return firebase.auth().signOut();
}).then(() => {
window.location.assign('/profile');
});
}
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
var postIdTokenToSessionLogin = function(url, idToken, csrfToken) {
// POST to session login endpoint.
return $.ajax({
type:'POST',
url: url,
dataType:"json",
data: {idToken: idToken, csrfToken: csrfToken},
contentType: 'application/x-www-form-urlencoded',
xhrFields: {
withCredentials: true
},
crossDomain: true
});
};
Run Code Online (Sandbox Code Playgroud)
这是我的服务器端代码
app.post("/sessionLogin", (req, res) => {
// Get ID token and CSRF token.
var idToken = req.body.idToken.toString();
var csrfToken = req.body.csrfToken.toString();
// Guard against CSRF attacks.
if (!req.cookies || csrfToken !== req.cookies._csrf) {
res.status(401).send('UNAUTHORIZED REQUEST!');
return;
}
// Set session expiration to 5 days.
var expiresIn = 60 * 60 * 24 * 5 * 1000;
// Create the session cookie. This will also verify the ID token in the
process.
// The session cookie will have the same claims as the ID token.
// We could also choose to enforce that the ID token auth_time is recent.
firebase.auth().verifyIdToken(idToken).then(function(decodedClaims) {
// In this case, we are enforcing that the user signed in in the last 5
minutes.
if (new Date().getTime() / 1000 - decodedClaims.auth_time < 5 * 60) {
return firebase.auth().createSessionCookie(idToken, {expiresIn:
expiresIn});
}
throw new Error('UNAUTHORIZED REQUEST!');
})
.then(function(sessionCookie) {
// Note httpOnly cookie will not be accessible from javascript.
// secure flag should be set to true in production.
var options = {maxAge: expiresIn, path: "/", httpOnly: false, secure: true
/** to test in localhost */};
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({status: 'success'}));
})
.catch(function(error) {
res.status(401).send('UNAUTHORIZED REQUEST!');
});
});
app.get("/profile", (req, res) => {
console.log('Cookies: ', req.cookies); //Empty object, 'Cookies: {}'
res.render("profile");
});
app.post("/profile", (req, res) => {
res.send(req.body.name);
console.log('Cookies: ', req.cookies); //Cookies object with csrf and
session token
});
Run Code Online (Sandbox Code Playgroud)
这工作正常,我能够通过每个 POST 请求将 cookie 传递到服务器。未经身份验证的用户无法发送 POST 请求。
请注意: 1. 开发时在POST请求中httpOnly: false **检查客户端是否记录了session。向客户端隐藏应该是真的。2. 由于某种原因,这只适用于 POST 请求,不适用于 GET 请求。我在这里提出了这个问题(评论可能会有所帮助)。3.我使用csurf npm 包来分配CSRF cookie。以下是当用户访问应用程序时在 cookie 中分配用户 csrf 令牌的代码。访问链接以获取有关使用的更多信息。
CSRF 用法:
app.get("/", csrfProtection, (req, res) => {
res.render("home");
});
Run Code Online (Sandbox Code Playgroud)
如果您认为有什么更好的办法,请告诉我。
| 归档时间: |
|
| 查看次数: |
1274 次 |
| 最近记录: |