@webda/core
Version:
Expose API with Lambda
128 lines • 4.28 kB
JavaScript
import { serialize as cookieSerialize } from "cookie";
/**
* Cookie cannot be more than 4096, so we split them by this constant
* @hidden
*/
const SPLIT = 4096;
/**
* Cookie Options
*/
export class CookieOptions {
/**
* Load with default value
* @param options
* @param httpContext
*/
constructor(options, httpContext) {
Object.assign(this, options);
this.httpOnly ?? (this.httpOnly = true);
this.path ?? (this.path = "/");
this.maxAge ?? (this.maxAge = 86400 * 7);
this.sameSite ?? (this.sameSite = "lax");
this.name ?? (this.name = "webda");
if (httpContext) {
if (this.domain === true) {
this.domain = httpContext.getHostName();
}
this.secure ?? (this.secure = httpContext.getProtocol() === "https:");
}
}
}
/**
* Object that handle the session
*
* To get stateless server the session is encrypted inside a cookie, so you should not store large amount of data in it
* If you need big session then i would suggest to use a Memcache store with the user.uuid as a key
*
* It is part of the core framework implementation, you should not rely on any method of this object, as the implementation can change if needed
* An object session is exposed by the framework, so use this one ( for now it is a SecureCookie.getProxy() but can evolve )
*
* The object use Object.observe if available or try Proxy in other case, so old JS VM won't run it
*
* You cannot store variables starting with _ in the session if you do use them they won't be persist, so it can be used to add some context
*
* @category CoreFeatures
*/
export class SecureCookie {
/**
* Load cookie
*
* It manages the split of the cookie to 4096 if required
*
* @param name cookie name
* @param options
* @param context
* @returns
*/
static async load(name, context, options) {
let cookies = {};
let raw = "";
let session = await context.newSession();
name ?? (name = "webda");
// No http context
if (!context.getHttpContext()) {
return session;
}
cookies = context.getHttpContext().getCookies();
if (cookies) {
raw = cookies[name] || "";
let j = 2;
let cookieName = `${name}${j++}`;
while (cookies[cookieName]) {
raw += cookies[cookieName];
cookieName = `${name}${j++}`;
}
}
// No cookie found or empty
if (raw === "") {
return session;
}
try {
return Object.assign(session, await context.getWebda().getCrypto().jwtVerify(raw, options));
}
catch (err) {
context.log("WARN", "Ignoring bad cookie", `'${raw}'`, "from", context.getHttpContext());
return session;
}
}
/**
* Send a cookie, split it if required
*
* @param name
* @param context
* @param data
* @param options
*/
static async save(name, context, data, options, cookieOptions) {
let value = await context.getWebda().getCrypto().jwtSign(Object.assign({}, data), options);
this.sendCookie(context, name, value, new CookieOptions(cookieOptions, context.getHttpContext()));
}
/**
* Will send the cookie to context and split it
* if needed as max length for cookies are 4096
* including the params (the whole Set-Cookie header)
*
* @param ctx to send cookie to
* @param name name of the cookie
* @param value of the cookie
* @param params for the cookie
*/
static sendCookie(ctx, name, value, params) {
let j = 1;
name ?? (name = "webda");
let cookieName = name;
let limit;
const mapLength = cookieSerialize(name, "", params).length;
for (let i = 0; i < value.length;) {
limit = SPLIT - mapLength;
if (j > 1) {
cookieName = `${name}${j}`;
limit--;
}
ctx.cookie(cookieName, value.substring(i, i + limit), params);
j++;
i += limit;
}
}
}
//# sourceMappingURL=cookie.js.map