ETA On SSR
martijnmelchers opened this issue · 8 comments
Is your feature request related to a problem? Please describe
A working version of SSR
Describe the solution you'd like
A working SSR solution
Describe alternatives you've considered
We're currently using ngx-cookie however this is not properly working as of 6.0.0 with Angular 13
Additional context
No response
i tried to implement the ssr support in the same cookies service class. Here is my solution which i am using currently:
import {Inject, Injectable, Optional, PLATFORM_ID} from '@angular/core';
import {DOCUMENT, isPlatformBrowser} from '@angular/common';
import {REQUEST} from "@nguniversal/express-engine/tokens";
import {Request} from "express";
export type SameSite = 'Lax' | 'None' | 'Strict';
export interface CookieOptions {
expires?: number | Date;
path?: string;
domain?: string;
secure?: boolean;
sameSite?: SameSite;
}
@Injectable({
providedIn: 'root',
})
export class CookieService {
private readonly documentIsAccessible: boolean;
constructor(
@Inject(DOCUMENT) private document: Document,
// Get the `PLATFORM_ID` so we can check if we're in a browser.
@Inject(PLATFORM_ID) private platformId: string,
@Optional() @Inject(REQUEST) private request: Request
) {
this.documentIsAccessible = isPlatformBrowser(this.platformId);
}
/**
* Get cookie Regular Expression
*
* @param name Cookie name
* @returns property RegExp
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
private static getCookieRegExp(name: string): RegExp {
const escapedName: string = name.replace(/([\[\]\{\}\(\)\|\=\;\+\?\,\.\*\^\$])/gi, '\\$1');
return new RegExp('(?:^' + escapedName + '|;\\s*' + escapedName + ')=(.*?)(?:;|$)', 'g');
}
/**
* Gets the unencoded version of an encoded component of a Uniform Resource Identifier (URI).
*
* @param encodedURIComponent A value representing an encoded URI component.
*
* @returns The unencoded version of an encoded component of a Uniform Resource Identifier (URI).
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
private static safeDecodeURIComponent(encodedURIComponent: string): string {
try {
return decodeURIComponent(encodedURIComponent);
} catch {
// probably it is not uri encoded. return as is
return encodedURIComponent;
}
}
/**
* Return `true` if {@link Document} is accessible, otherwise return `false`
*
* @param name Cookie name
* @returns boolean - whether cookie with specified name exists
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
check(name: string): boolean {
name = encodeURIComponent(name);
const regExp: RegExp = CookieService.getCookieRegExp(name);
return regExp.test(this.getPlatformCookies());
}
/**
* Get cookies by name
*
* @param name Cookie name
* @returns property value
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
get(name: string): string {
if (this.check(name)) {
name = encodeURIComponent(name);
const regExp: RegExp = CookieService.getCookieRegExp(name);
const result: RegExpExecArray | null = regExp.exec(this.getPlatformCookies());
return result && result[1] ? CookieService.safeDecodeURIComponent(result[1]) : '';
} else {
return '';
}
}
/**
* Get all cookies in JSON format
*
* @returns all the cookies in json
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
getAll(): { [key: string]: string } {
const cookies: { [key: string]: string } = {};
const cookiesString: string = this.getPlatformCookies();
if (cookiesString) {
cookiesString.split(';').forEach((currentCookie: string) => {
const [cookieName, cookieValue] = currentCookie.split('=');
cookies[CookieService.safeDecodeURIComponent(cookieName.replace(/^ /, ''))] = CookieService.safeDecodeURIComponent(cookieValue);
});
}
return cookies;
}
/**
* Set cookie based on provided information
*
* @param name Cookie name
* @param value Cookie value
* @param expires Number of days until the cookies expires or an actual `Date`
* @param path Cookie path
* @param domain Cookie domain
* @param secure Secure flag
* @param sameSite OWASP samesite token `Lax`, `None`, or `Strict`. Defaults to `Lax`
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
set(
name: string,
value: string,
expires?: CookieOptions['expires'],
path?: CookieOptions['path'],
domain?: CookieOptions['domain'],
secure?: CookieOptions['secure'],
sameSite?: SameSite
): void;
/**
* Set cookie based on provided information
*
* Cookie's parameters:
* <pre>
* expires Number of days until the cookies expires or an actual `Date`
* path Cookie path
* domain Cookie domain
* secure Secure flag
* sameSite OWASP samesite token `Lax`, `None`, or `Strict`. Defaults to `Lax`
* </pre>
*
* @param name Cookie name
* @param value Cookie value
* @param options Body with cookie's params
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
set(name: string, value: string, options?: CookieOptions): void;
set(
name: string,
value: string,
expiresOrOptions?: CookieOptions['expires'] | CookieOptions,
path?: CookieOptions['path'],
domain?: CookieOptions['domain'],
secure?: CookieOptions['secure'],
sameSite?: SameSite
): void {
if (!this.documentIsAccessible) {
return;
}
if (typeof expiresOrOptions === 'number' || expiresOrOptions instanceof Date || path || domain || secure || sameSite) {
const optionsBody = {
expires: expiresOrOptions as CookieOptions['expires'],
path,
domain,
secure,
sameSite: sameSite ? sameSite : 'Lax',
};
this.set(name, value, optionsBody);
return;
}
let cookieString: string = encodeURIComponent(name) + '=' + encodeURIComponent(value) + ';';
const options = expiresOrOptions ? expiresOrOptions : {};
if (options.expires) {
if (typeof options.expires === 'number') {
const dateExpires: Date = new Date(new Date().getTime() + options.expires * 1000 * 60 * 60 * 24);
cookieString += 'expires=' + dateExpires.toUTCString() + ';';
} else {
cookieString += 'expires=' + options.expires.toUTCString() + ';';
}
}
if (options.path) {
cookieString += 'path=' + options.path + ';';
}
if (options.domain) {
cookieString += 'domain=' + options.domain + ';';
}
if (options.secure === false && options.sameSite === 'None') {
options.secure = true;
console.warn(
`[ngx-cookie-service] Cookie ${name} was forced with secure flag because sameSite=None.` +
`More details : https://github.com/stevermeister/ngx-cookie-service/issues/86#issuecomment-597720130`
);
}
if (options.secure) {
cookieString += 'secure;';
}
if (!options.sameSite) {
options.sameSite = 'Lax';
}
cookieString += 'sameSite=' + options.sameSite + ';';
// this.document.cookie = cookieString
this.setPlatformCookies(cookieString);
}
/**
* Delete cookie by name
*
* @param name Cookie name
* @param path Cookie path
* @param domain Cookie domain
* @param secure Cookie secure flag
* @param sameSite Cookie sameSite flag - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
delete(name: string, path?: CookieOptions['path'], domain?: CookieOptions['domain'], secure?: CookieOptions['secure'], sameSite: SameSite = 'Lax'): void {
if (!this.documentIsAccessible) {
return;
}
const expiresDate = new Date('Thu, 01 Jan 1970 00:00:01 GMT');
this.set(name, '', {expires: expiresDate, path, domain, secure, sameSite});
}
/**
* Delete all cookies
*
* @param path Cookie path
* @param domain Cookie domain
* @param secure Is the Cookie secure
* @param sameSite Is the cookie same site
*
* @author: Stepan Suvorov
* @since: 1.0.0
*/
deleteAll(path?: CookieOptions['path'], domain?: CookieOptions['domain'], secure?: CookieOptions['secure'], sameSite: SameSite = 'Lax'): void {
if (!this.documentIsAccessible) {
return;
}
const cookies: any = this.getAll();
for (const cookieName in cookies) {
if (cookies.hasOwnProperty(cookieName)) {
this.delete(cookieName, path, domain, secure, sameSite);
}
}
}
getPlatformCookies(): string {
let cookies = '';
if (this.documentIsAccessible) {
cookies = this.document.cookie;
} else {
cookies = this.request.headers.cookie != undefined ? this.request.headers.cookie : '';
}
return cookies;
}
setPlatformCookies(cookieString: string) {
if (this.documentIsAccessible) {
this.document.cookie = cookieString;
} else {
this.request.headers.cookie = cookieString;
}
}
}
@martijnmelchers for some reason I didn't get notification for this issue. First, what error you see?
Second, we currently have 2 solutions (#214 and #206) for this and one of it will make it to production. I will keep the issue open and let you know the progress.
is there a solution yet?
is there a solution yet?
Haven't got a chance to work on this. But current version 14.0.0
should work with SSR and shows login page for brief period of time and we are working to fix this.
is there a solution yet?
Haven't got a chance to work on this. But current version
14.0.0
should work with SSR and shows login page for brief period of time and we are working to fix this.
You should check out other libraries doing the same. We've been using ngx-cookie for a while now and it's been working fine. However 6.0 broke because of a parsing error. Our only hold back to switching to this library is a stable working SSR version
is there a solution yet?
Haven't got a chance to work on this. But current version
14.0.0
should work with SSR and shows login page for brief period of time and we are working to fix this.You should check out other libraries doing the same. We've been using ngx-cookie for a while now and it's been working fine. However 6.0 broke because of a parsing error. Our only hold back to switching to this library is a stable working SSR version
Understood. BTW SSR always worked in this library v12, v13 etc... We are in the process of adding dedicated SSR support
@martijnmelchers @viniciusverasdossantos @bahag-mersinlioglue Just now published version 14.0.0-beta.0 to NPM. Could you guys try it and let me know how it goes.
I am closing since I have not heard from anyone. If anyone finds this comment, you can install SSR version of the library and usage guide here