PHP 8.2.30
Preview: login.handler.ts Size: 4.25 KB
//proc/thread-self/root/home/byroehnu/.trash/node_modules11/@adminjs/express/src/authentication/login.handler.ts

import AdminJS from "adminjs";
import { Router } from "express";

import type {
  AuthenticationContext,
  AuthenticationMaxRetriesOptions,
  AuthenticationOptions,
} from "../types.js";
import { INVALID_AUTH_CONFIG_ERROR, WrongArgumentError } from "../errors.js";

const getLoginPath = (admin: AdminJS): string => {
  const { loginPath, rootPath } = admin.options;
  // since we are inside already namespaced router we have to replace login and logout routes that
  // they don't have rootUrl inside. So changing /admin/login to just /login.
  // but there is a case where user gives / as a root url and /login becomes `login`. We have to
  // fix it by adding / in front of the route
  const normalizedLoginPath = loginPath.replace(rootPath, "");

  return normalizedLoginPath.startsWith("/")
    ? normalizedLoginPath
    : `/${normalizedLoginPath}`;
};

class Retry {
  private static retriesContainer: Map<string, Retry> = new Map();
  private lastRetry: Date | undefined;
  private retriesCount = 0;

  constructor(ip: string) {
    const existing = Retry.retriesContainer.get(ip);
    if (existing) {
      return existing;
    }
    Retry.retriesContainer.set(ip, this);
  }

  public canLogin(
    maxRetries: number | AuthenticationMaxRetriesOptions | undefined
  ): boolean {
    if (maxRetries === undefined) {
      return true;
    } else if (typeof maxRetries === "number") {
      maxRetries = {
        count: maxRetries,
        duration: 60,
      };
    } else if (maxRetries.count <= 0) {
      return true;
    }
    if (
      !this.lastRetry ||
      new Date().getTime() - this.lastRetry.getTime() >
        maxRetries.duration * 1000
    ) {
      this.lastRetry = new Date();
      this.retriesCount = 1;
      return true;
    } else {
      this.lastRetry = new Date();
      this.retriesCount++;
      return this.retriesCount <= maxRetries.count;
    }
  }
}

export const withLogin = (
  router: Router,
  admin: AdminJS,
  auth: AuthenticationOptions
): void => {
  const { rootPath } = admin.options;
  const loginPath = getLoginPath(admin);

  const { provider } = auth;
  const providerProps = provider?.getUiProps?.() ?? {};

  router.get(loginPath, async (req, res) => {
    const baseProps = {
      action: admin.options.loginPath,
      errorMessage: null,
    };
    const login = await admin.renderLogin({
      ...baseProps,
      ...providerProps,
    });

    return res.send(login);
  });

  router.post(loginPath, async (req, res, next) => {
    if (!new Retry(req.ip).canLogin(auth.maxRetries)) {
      const login = await admin.renderLogin({
        action: admin.options.loginPath,
        errorMessage: "tooManyRequests",
        ...providerProps,
      });

      return res.send(login);
    }

    const context: AuthenticationContext = { req, res };

    let adminUser;
    try {
      if (provider) {
        adminUser = await provider.handleLogin(
          {
            headers: req.headers,
            query: req.query,
            params: req.params,
            data: req.fields ?? {},
          },
          context
        );
      } else if (auth.authenticate) {
        const { email, password } = req.fields as {
          email: string;
          password: string;
        };
        // "auth.authenticate" must always be defined if "auth.provider" isn't
        adminUser = await auth.authenticate(email, password, context);
      } else {
        throw new WrongArgumentError(INVALID_AUTH_CONFIG_ERROR);
      }
    } catch (error) {
      const errorMessage = error.message || error.error || "invalidCredentials";

      const loginPage = await admin.renderLogin({
        action: admin.options.loginPath,
        errorMessage,
        ...providerProps,
      });

      return res.status(400).send(loginPage);
    }

    if (adminUser) {
      req.session.adminUser = adminUser;
      req.session.save((err) => {
        if (err) {
          return next(err);
        }
        if (req.session.redirectTo) {
          return res.redirect(302, req.session.redirectTo);
        } else {
          return res.redirect(302, rootPath);
        }
      });
    } else {
      const login = await admin.renderLogin({
        action: admin.options.loginPath,
        errorMessage: "invalidCredentials",
        ...providerProps,
      });

      return res.send(login);
    }
  });
};

Directory Contents

Dirs: 0 × Files: 4

Name Size Perms Modified Actions
4.25 KB lrw-r--r-- 2026-02-28 00:29:04
Edit Download
935 B lrw-r--r-- 2026-02-28 00:29:06
Edit Download
490 B lrw-r--r-- 2026-02-28 00:29:10
Edit Download
1.53 KB lrw-r--r-- 2026-02-28 00:29:10
Edit Download

If ZipArchive is unavailable, a .tar will be created (no compression).