106 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			106 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) Sapphirecode - All Rights Reserved
 | 
						|
 * This file is part of Auth-Server-Helper which is released under MIT.
 | 
						|
 * See file 'LICENSE' for full license details.
 | 
						|
 * Created by Timo Hocker <timo@scode.ovh>, December 2020
 | 
						|
 */
 | 
						|
 | 
						|
import { IncomingMessage, ServerResponse } from 'http';
 | 
						|
import { run_regex } from '@sapphirecode/utilities';
 | 
						|
import authority from './Authority';
 | 
						|
 | 
						|
type AnyFunc = (...args: unknown[]) => unknown;
 | 
						|
type Gateway = (
 | 
						|
  req: IncomingMessage,
 | 
						|
  res: ServerResponse, next: AnyFunc
 | 
						|
) => unknown;
 | 
						|
 | 
						|
interface GatewayOptions {
 | 
						|
  redirect_url?: string;
 | 
						|
  cookie_name?: string;
 | 
						|
}
 | 
						|
 | 
						|
class GatewayClass {
 | 
						|
  private _options: GatewayOptions;
 | 
						|
 | 
						|
  public constructor (options: GatewayOptions = {}) {
 | 
						|
    this._options = options;
 | 
						|
  }
 | 
						|
 | 
						|
  public deny (res: ServerResponse): void {
 | 
						|
    res.statusCode = 403;
 | 
						|
    res.end();
 | 
						|
  }
 | 
						|
 | 
						|
  public redirect (res: ServerResponse): void {
 | 
						|
    if (typeof this._options.redirect_url !== 'string')
 | 
						|
      return this.deny(res);
 | 
						|
 | 
						|
    res.statusCode = 302;
 | 
						|
    res.setHeader ('Location', this._options.redirect_url);
 | 
						|
    res.end ();
 | 
						|
  }
 | 
						|
 | 
						|
  public get_header_auth (req: IncomingMessage): string | null {
 | 
						|
    const auth_header = req.headers.authorization;
 | 
						|
    const auth = (/(?<type>\w+) (?<data>.*)/u).exec (auth_header || '');
 | 
						|
    if (auth === null)
 | 
						|
      return null;
 | 
						|
    if (auth.groups?.type !== 'Bearer')
 | 
						|
      return null;
 | 
						|
    return auth.groups?.data;
 | 
						|
  }
 | 
						|
 | 
						|
  public get_cookie_auth (req: IncomingMessage): string | null {
 | 
						|
    if (typeof this._options.cookie_name === 'undefined')
 | 
						|
      return null;
 | 
						|
    let auth = null;
 | 
						|
    run_regex (
 | 
						|
      /(?:^|;)\s*(?<name>[^;=]+)=(?<value>[^;]+)/gu,
 | 
						|
      req.headers.cookie,
 | 
						|
      (res: RegExpMatchArray) => {
 | 
						|
        if (res.groups?.name === this._options.cookie_name)
 | 
						|
          auth = res.groups?.value;
 | 
						|
      }
 | 
						|
    );
 | 
						|
    return auth;
 | 
						|
  }
 | 
						|
 | 
						|
  public authenticate (req: IncomingMessage): boolean {
 | 
						|
    let auth = this.get_header_auth (req);
 | 
						|
    if (auth === null)
 | 
						|
      auth = this.get_cookie_auth (req);
 | 
						|
    if (auth === null)
 | 
						|
      return false;
 | 
						|
 | 
						|
    const ver = authority.verify (auth);
 | 
						|
 | 
						|
    const con = req.connection as unknown as Record<string, unknown>;
 | 
						|
    con.auth = { token_id: ver.id, token_data: ver.data };
 | 
						|
 | 
						|
    return ver.authorized;
 | 
						|
  }
 | 
						|
 | 
						|
  public process_request (
 | 
						|
    req: IncomingMessage,
 | 
						|
    res: ServerResponse,
 | 
						|
    next: AnyFunc
 | 
						|
  ): unknown {
 | 
						|
    if (this.authenticate (req))
 | 
						|
      return next ();
 | 
						|
    return this.redirect (res);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
export default function create_gateway (options: GatewayOptions): Gateway {
 | 
						|
  const g = new GatewayClass (options);
 | 
						|
  return g.process_request.bind (g);
 | 
						|
}
 | 
						|
 | 
						|
export {
 | 
						|
  AnyFunc,
 | 
						|
  Gateway,
 | 
						|
  GatewayOptions,
 | 
						|
  GatewayClass
 | 
						|
};
 |