Fix auth redirect_uri chain
This commit is contained in:
parent
634edda704
commit
eb935bf52a
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wms-core",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.0",
|
||||
"description": "Node web framework",
|
||||
"repository": "git@gitlab.com:ArisuOngaku/wms-core.git",
|
||||
"author": "Alice Gaudon <alice@gaudon.pro>",
|
||||
|
@ -4,11 +4,13 @@ import config from "config";
|
||||
import Logger from "./Logger";
|
||||
import Validator, {FileError, ValidationBag} from "./db/Validator";
|
||||
import FileUploadMiddleware from "./FileUploadMiddleware";
|
||||
import * as querystring from "querystring";
|
||||
import {ParsedUrlQueryInput} from "querystring";
|
||||
|
||||
export default abstract class Controller {
|
||||
private static readonly routes: { [p: string]: string } = {};
|
||||
|
||||
public static route(route: string, params: RouteParams = [], absolute: boolean = false): string {
|
||||
public static route(route: string, params: RouteParams = [], query: ParsedUrlQueryInput = {}, absolute: boolean = false): string {
|
||||
let path = this.routes[route];
|
||||
if (path === undefined) throw new Error(`Unknown route for name ${route}.`);
|
||||
|
||||
@ -31,7 +33,8 @@ export default abstract class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
return `${absolute ? config.get<string>('public_url') : ''}${path}`;
|
||||
const queryStr = querystring.stringify(query);
|
||||
return `${absolute ? config.get<string>('public_url') : ''}${path}` + (queryStr.length > 0 ? '?' + queryStr : '');
|
||||
}
|
||||
|
||||
private readonly router: Router = express.Router();
|
||||
|
@ -5,12 +5,8 @@ import nunjucks from 'nunjucks';
|
||||
import * as util from "util";
|
||||
import {WrappingError} from "./Utils";
|
||||
import mjml2html from "mjml";
|
||||
import * as querystring from "querystring";
|
||||
import Logger from "./Logger";
|
||||
|
||||
export function mailRoute(template: string): string {
|
||||
return `/mail/${template}`;
|
||||
}
|
||||
import Controller from "./Controller";
|
||||
|
||||
export default class Mail {
|
||||
private static transporter: Transporter;
|
||||
@ -101,7 +97,7 @@ export default class Mail {
|
||||
// Set data
|
||||
this.data.mail_subject = this.options.subject;
|
||||
this.data.mail_to = this.options.to;
|
||||
this.data.mail_link = `${config.get<string>('public_url')}${mailRoute(this.template.template)}?${querystring.stringify(this.data)}`;
|
||||
this.data.mail_link = config.get<string>('public_url') + Controller.route('mail', [this.template.template], this.data);
|
||||
this.data.app = config.get('app');
|
||||
|
||||
// Log
|
||||
|
@ -3,7 +3,6 @@ import {NextFunction, Request, Response, Router} from "express";
|
||||
import AuthGuard from "./AuthGuard";
|
||||
import Controller from "../Controller";
|
||||
import {ForbiddenHttpError} from "../HttpError";
|
||||
import * as querystring from "querystring";
|
||||
|
||||
export default class AuthComponent extends ApplicationComponent<void> {
|
||||
private readonly authGuard: AuthGuard<any>;
|
||||
@ -25,7 +24,7 @@ export default class AuthComponent extends ApplicationComponent<void> {
|
||||
export const REQUIRE_REQUEST_AUTH_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
if (!await req.authGuard.isAuthenticatedViaRequest(req)) {
|
||||
req.flash('error', `You must be logged in to access ${req.url}.`);
|
||||
res.redirect((Controller.route('auth') || '/') + '?' + querystring.stringify({
|
||||
res.redirect(Controller.route('auth', undefined, {
|
||||
redirect_uri: req.url,
|
||||
}));
|
||||
return;
|
||||
@ -42,7 +41,7 @@ export const REQUIRE_AUTH_MIDDLEWARE = async (req: Request, res: Response, next:
|
||||
} else {
|
||||
if (!await req.authGuard.isAuthenticated(req.session!)) {
|
||||
req.flash('error', `You must be logged in to access ${req.url}.`);
|
||||
res.redirect((Controller.route('auth') || '/') + '?' + querystring.stringify({
|
||||
res.redirect(Controller.route('auth', undefined, {
|
||||
redirect_uri: req.url,
|
||||
}));
|
||||
return;
|
||||
|
@ -28,7 +28,7 @@ export default abstract class AuthController extends Controller {
|
||||
protected async postLogout(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
await req.authGuard.logout(req.session!);
|
||||
req.flash('success', 'Successfully logged out.');
|
||||
res.redirectBack('/');
|
||||
res.redirect(req.query.redirect_uri?.toString() || '/');
|
||||
}
|
||||
|
||||
}
|
@ -4,7 +4,7 @@ import Mail from "../Mail";
|
||||
|
||||
export default class MailController extends Controller {
|
||||
routes(): void {
|
||||
this.get("/mail/:template", this.getMail);
|
||||
this.get("/mail/:template", this.getMail, 'mail');
|
||||
}
|
||||
|
||||
private async getMail(request: Request, response: Response) {
|
||||
|
@ -8,6 +8,7 @@ import {MailTemplate} from "../../Mail";
|
||||
import {AuthError, PendingApprovalAuthError} from "../AuthGuard";
|
||||
import geoip from "geoip-lite";
|
||||
import AuthController from "../AuthController";
|
||||
import NunjucksComponent from "../../components/NunjucksComponent";
|
||||
|
||||
|
||||
export default abstract class MagicLinkAuthController extends AuthController {
|
||||
@ -30,7 +31,7 @@ export default abstract class MagicLinkAuthController extends AuthController {
|
||||
},
|
||||
html: () => {
|
||||
req.flash('warning', `Your account is pending review. You'll receive an email once you're approved.`);
|
||||
res.redirectBack('/');
|
||||
res.redirect('/');
|
||||
}
|
||||
});
|
||||
return;
|
||||
@ -49,14 +50,16 @@ export default abstract class MagicLinkAuthController extends AuthController {
|
||||
this.magicLinkMailTemplate = magicLinkMailTemplate;
|
||||
}
|
||||
|
||||
protected async getAuth(request: Request, response: Response, next: NextFunction): Promise<void> {
|
||||
const link = await MagicLink.bySessionID(request.sessionID!, [this.loginMagicLinkActionType, this.registerMagicLinkActionType]);
|
||||
protected async getAuth(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const link = await MagicLink.bySessionID(req.sessionID!, [this.loginMagicLinkActionType, this.registerMagicLinkActionType]);
|
||||
if (link && await link.isValid()) {
|
||||
response.redirect(Controller.route('magic_link_lobby'));
|
||||
res.redirect(Controller.route('magic_link_lobby', undefined, {
|
||||
redirect_uri: req.query.redirect_uri?.toString() || undefined,
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
await super.getAuth(request, response, next);
|
||||
await super.getAuth(req, res, next);
|
||||
}
|
||||
|
||||
protected async postAuth(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
@ -81,21 +84,27 @@ export default abstract class MagicLinkAuthController extends AuthController {
|
||||
await MagicLinkController.sendMagicLink(
|
||||
req.sessionID!,
|
||||
isRegistration ? this.registerMagicLinkActionType : this.loginMagicLinkActionType,
|
||||
Controller.route('auth'),
|
||||
Controller.route('auth', undefined, {
|
||||
redirect_uri: req.query.redirect_uri?.toString() || undefined,
|
||||
}),
|
||||
email,
|
||||
this.magicLinkMailTemplate,
|
||||
{
|
||||
type: isRegistration ? 'register' : 'login',
|
||||
ip: req.ip,
|
||||
geo: geo ? `${geo.city}, ${geo.country}` : 'Unknown location',
|
||||
},
|
||||
req,
|
||||
res
|
||||
}
|
||||
);
|
||||
|
||||
res.redirect(Controller.route('magic_link_lobby', undefined, {
|
||||
redirect_uri: req.query.redirect_uri?.toString() || NunjucksComponent.getPreviousURL(req),
|
||||
}));
|
||||
} else {
|
||||
// Confirm registration req
|
||||
req.flash('register_confirm_email', email);
|
||||
res.redirect(Controller.route('auth'));
|
||||
res.redirect(Controller.route('auth', undefined, {
|
||||
redirect_uri: req.query.redirect_uri?.toString() || undefined,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,38 +2,33 @@ import Controller from "../../Controller";
|
||||
import {Request, Response} from "express";
|
||||
import MagicLinkWebSocketListener from "./MagicLinkWebSocketListener";
|
||||
import {BadRequestError, NotFoundHttpError} from "../../HttpError";
|
||||
import querystring from "querystring";
|
||||
import Throttler from "../../Throttler";
|
||||
import Mail, {MailTemplate} from "../../Mail";
|
||||
import MagicLink from "../models/MagicLink";
|
||||
import config from "config";
|
||||
|
||||
export default abstract class MagicLinkController extends Controller {
|
||||
public static async sendMagicLink(sessionID: string, actionType: string, original_url: string, email: string, mailTemplate: MailTemplate, data: object, req: Request, res: Response): Promise<void> {
|
||||
public static async sendMagicLink(sessionID: string, actionType: string, original_url: string, email: string, mailTemplate: MailTemplate, data: object): Promise<void> {
|
||||
Throttler.throttle('magic_link', 2, MagicLink.validityPeriod(), sessionID, 0, 0);
|
||||
Throttler.throttle('magic_link', 1, MagicLink.validityPeriod(), email, 0, 0);
|
||||
|
||||
let link = await MagicLink.bySessionID(sessionID, actionType);
|
||||
if (!link) {
|
||||
link = new MagicLink({
|
||||
const link = await MagicLink.bySessionID(sessionID, actionType) ||
|
||||
new MagicLink({
|
||||
session_id: sessionID,
|
||||
action_type: actionType,
|
||||
original_url: original_url,
|
||||
});
|
||||
}
|
||||
|
||||
const token = await link.generateToken(email);
|
||||
await link.save();
|
||||
|
||||
// Send email
|
||||
await new Mail(mailTemplate, Object.assign(data, {
|
||||
link: `${config.get<string>('base_url')}${Controller.route('magic_link')}?${querystring.stringify({
|
||||
link: `${config.get<string>('base_url')}${Controller.route('magic_link', undefined, {
|
||||
id: link.id,
|
||||
token: token,
|
||||
})}`,
|
||||
})).send(email);
|
||||
|
||||
res.redirect(Controller.route('magic_link_lobby'));
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,8 +8,6 @@ export default class FormHelperComponent extends ApplicationComponent<void> {
|
||||
throw new Error('Session is unavailable.');
|
||||
}
|
||||
|
||||
res.locals.query = req.query;
|
||||
|
||||
let _validation: any = null;
|
||||
res.locals.validation = () => {
|
||||
if (!_validation) {
|
||||
|
@ -5,10 +5,14 @@ import ApplicationComponent from "../ApplicationComponent";
|
||||
import Controller from "../Controller";
|
||||
import {ServerError} from "../HttpError";
|
||||
import * as querystring from "querystring";
|
||||
import {ParsedUrlQueryInput} from "querystring";
|
||||
|
||||
export default class NunjucksComponent extends ApplicationComponent<void> {
|
||||
public static getPreviousURL(req: Request, defaultUrl?: string): string {
|
||||
return req.query.redirect_uri?.toString() || req.headers.referer?.[0] || defaultUrl || '/';
|
||||
let referrer = req.headers.referer?.toString() || req.headers.referrer?.toString() || defaultUrl || '/';
|
||||
if (referrer.startsWith('https://') || referrer.startsWith('http://')) return '/';
|
||||
else if (!referrer.startsWith('/')) referrer = req.url + (req.url.endsWith('/') ? '' : '/') + referrer;
|
||||
return referrer;
|
||||
}
|
||||
|
||||
private readonly viewsPath: string;
|
||||
@ -40,8 +44,8 @@ export default class NunjucksComponent extends ApplicationComponent<void> {
|
||||
noCache: !config.get('view.cache'),
|
||||
throwOnUndefined: true,
|
||||
})
|
||||
.addGlobal('route', (route: string, params: { [p: string]: string } | [] = [], absolute: boolean = false) => {
|
||||
const path = Controller.route(route, params, absolute);
|
||||
.addGlobal('route', (route: string, params?: { [p: string]: string } | [], query?: ParsedUrlQueryInput, absolute?: boolean) => {
|
||||
const path = Controller.route(route, params, query, absolute);
|
||||
if (path === null) throw new ServerError(`Route ${route} not found.`);
|
||||
return path;
|
||||
})
|
||||
@ -58,7 +62,9 @@ export default class NunjucksComponent extends ApplicationComponent<void> {
|
||||
router.use((req, res, next) => {
|
||||
req.env = this.env!;
|
||||
res.locals.url = req.url;
|
||||
res.locals.params = () => req.params;
|
||||
res.locals.params = req.params;
|
||||
res.locals.query = req.query;
|
||||
res.locals.body = req.body;
|
||||
|
||||
res.locals.app = config.get('app');
|
||||
|
||||
|
@ -8,7 +8,11 @@
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<div class="panel">
|
||||
{% set action = route('auth') + '?' + querystring.stringify({redirect_uri: req.url}) %}
|
||||
{% set queryStr = '' %}
|
||||
{% if query.redirect_uri | length %}
|
||||
{% set queryStr = '?' + querystring.stringify({redirect_uri: query.redirect_uri}) %}
|
||||
{% endif %}
|
||||
{% set action = route('auth') + queryStr %}
|
||||
|
||||
{% if register_confirm_email %}
|
||||
<form action="{{ action }}" method="POST" id="register-form">
|
||||
|
Loading…
Reference in New Issue
Block a user