improved cookie security
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@ -37,6 +37,10 @@ interface AccessResponse {
|
||||
type AuthHandler =
|
||||
(req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
|
||||
|
||||
function build_cookie (name: string, value: string): string {
|
||||
return `${name}=${value}; Secure; HttpOnly; SameSite=Strict`;
|
||||
}
|
||||
|
||||
class AuthRequest {
|
||||
public request: IncomingMessage;
|
||||
public response: ServerResponse;
|
||||
@ -87,7 +91,7 @@ class AuthRequest {
|
||||
this.response.setHeader ('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
// eslint-disable-next-line max-statements, max-lines-per-function
|
||||
public async allow_access ({
|
||||
access_token_expires_in,
|
||||
include_refresh_token,
|
||||
@ -116,7 +120,7 @@ class AuthRequest {
|
||||
const cookies = [];
|
||||
|
||||
if (typeof this._cookie_name === 'string')
|
||||
cookies.push (`${this._cookie_name}=${at.signature}`);
|
||||
cookies.push (build_cookie (this._cookie_name, at.signature));
|
||||
|
||||
if (include_refresh_token) {
|
||||
logger ('including refresh token');
|
||||
@ -132,7 +136,8 @@ class AuthRequest {
|
||||
result.refresh_token_id = rt.id;
|
||||
|
||||
if (typeof this._refresh_cookie_name === 'string')
|
||||
cookies.push (`${this._refresh_cookie_name}=${rt.signature}`);
|
||||
// eslint-disable-next-line max-len
|
||||
cookies.push (build_cookie (this._refresh_cookie_name, rt.signature));
|
||||
}
|
||||
|
||||
if (cookies.length > 0) {
|
||||
|
@ -6,10 +6,10 @@
|
||||
*/
|
||||
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import { run_regex } from '@sapphirecode/utilities';
|
||||
import authority from './Authority';
|
||||
import { AuthRequest, AccessSettings } from './AuthHandler';
|
||||
import { debug } from './debug';
|
||||
import { extract_cookie } from './cookie';
|
||||
|
||||
const logger = debug ('gateway');
|
||||
|
||||
@ -32,11 +32,6 @@ interface GatewayOptions {
|
||||
refresh_settings?: RefreshSettings;
|
||||
}
|
||||
|
||||
interface AuthCookies {
|
||||
access_cookie: string | null;
|
||||
refresh_cookie: string | null;
|
||||
}
|
||||
|
||||
class GatewayClass {
|
||||
private _options: GatewayOptions;
|
||||
|
||||
@ -81,37 +76,11 @@ class GatewayClass {
|
||||
return auth.groups?.data;
|
||||
}
|
||||
|
||||
public get_cookie_auth (req: IncomingMessage): AuthCookies {
|
||||
logger ('extracting tokens from cookies');
|
||||
const result: AuthCookies = {
|
||||
access_cookie: null,
|
||||
refresh_cookie: null
|
||||
};
|
||||
|
||||
const cookie_regex = /(?:^|;)\s*(?<name>[^;=]+)=(?<value>[^;]+)/gu;
|
||||
|
||||
run_regex (
|
||||
cookie_regex,
|
||||
req.headers.cookie,
|
||||
(res: RegExpMatchArray) => {
|
||||
logger ('parsing cookie %s', res.groups?.name);
|
||||
if (res.groups?.name === this._options.cookie_name)
|
||||
result.access_cookie = res.groups?.value as string;
|
||||
else if (res.groups?.name === this._options.refresh_cookie_name)
|
||||
result.refresh_cookie = res.groups?.value as string;
|
||||
}
|
||||
);
|
||||
|
||||
logger ('parsed cookies: %O', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public try_access (req: IncomingMessage): boolean {
|
||||
logger ('authenticating incoming request');
|
||||
const cookies = this.get_cookie_auth (req);
|
||||
let auth = this.get_header_auth (req);
|
||||
if (auth === null)
|
||||
auth = cookies.access_cookie;
|
||||
auth = extract_cookie (this._options.cookie_name, req.headers.cookie);
|
||||
if (auth === null) {
|
||||
logger ('found no auth token');
|
||||
return false;
|
||||
@ -139,7 +108,10 @@ class GatewayClass {
|
||||
|
||||
logger ('trying to apply refresh token');
|
||||
|
||||
const refresh = this.get_cookie_auth (req).refresh_cookie;
|
||||
const refresh = extract_cookie (
|
||||
this._options.refresh_cookie_name,
|
||||
req.headers.cookie
|
||||
);
|
||||
if (refresh === null) {
|
||||
logger ('could not find refresh token');
|
||||
return false;
|
||||
|
35
lib/cookie.ts
Normal file
35
lib/cookie.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { run_regex } from '@sapphirecode/utilities';
|
||||
import { debug } from './debug';
|
||||
|
||||
const logger = debug ('cookies');
|
||||
|
||||
function build_cookie (name: string, value: string): string {
|
||||
return `${name}=${value}; Secure; HttpOnly; SameSite=Strict`;
|
||||
}
|
||||
|
||||
function extract_cookie (
|
||||
name: string|undefined,
|
||||
header: string|undefined
|
||||
): string| null {
|
||||
logger (`extracting cookie ${name}`);
|
||||
|
||||
const cookie_regex = /(?:^|;)\s*(?<name>[^;=]+)=(?<value>[^;]+)/gu;
|
||||
|
||||
let result = null;
|
||||
|
||||
run_regex (
|
||||
cookie_regex,
|
||||
header,
|
||||
(res: RegExpMatchArray) => {
|
||||
logger ('parsing cookie %s', res.groups?.name);
|
||||
if (res.groups?.name === name) {
|
||||
logger ('found cookie');
|
||||
result = res.groups?.value as string;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export { build_cookie, extract_cookie };
|
Reference in New Issue
Block a user