1870da7f6f auth-providers: OIDC callback hardening (#10931)
* fix(authProviders): instrument OIDC callback + redirect on fail (not 500)

Production-blocking: every OIDC callback (bogus or real code) returned
500 Internal Server Error, even with valid sessions. No log was emitted
between `try auth via openid` and the 500 — passport strategy threw
silently before `handleProviderAuth` could log.

Per Codex plan-review amendments:
- Switch to explicit-callback passport.authenticate variant so strategy
  errors surface as err/info/status args instead of being swallowed.
- Privacy-safe diagnostic shape (no raw code/state/tokens/user). Fields:
  stage, errMessage/Name/Stack, hasUser, infoSummary, statusCode,
  hasSession + sessionKeys (names only), cookie + state + code
  PRESENCE (lengths only), host, forwardedProto.
- Permanent invalid-callback guard: ALL failures → 302 to /login with
  ?error=oidc_callback_failed, never 500. Same wrap covers
  handleProviderAuth throws.

Image-tag: hardcoreeng/account:integration-wac-2026-06-21-codex-r7.

Next step (P0-T2): user attempts login, we capture the actual error
from the diagnostic, then ship a surgical fix in r8.

Signed-off-by: Michael Uray <michael.uray@gmail.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* fix(account): admin.ts trim+lowercase on both env-set AND lookup

Codex plan-review Critical: server/account/src/admin.ts:15-18 built the
ADMIN_EMAILS set without normalizing, AND isAdminEmail() didn't normalize
the input. Today value is clean so isAdmin works, but a future env-value
like ADMIN_EMAILS=" Michael@Uray.io " would silently break admin
detection (Set lookup would miss the lowercased input).

Mongo backend already has this hardening (collections/mongo.ts:1141-1147).
Postgres + this helper didn't.

Now BOTH sides normalize: env split -> trim -> toLowerCase -> filter
non-empty + warn on no-@ entries (Codex Optional: warn not reject --
too sharp for a latent fix). Lookup also trim().toLowerCase().

Standalone upstream-PR-able per Codex Q-I3.

Tests cover whitespace, case-insensitive env, case-insensitive lookup,
invalid-shape warn, empty/unset env, multi-entry.

Signed-off-by: Michael Uray <michael.uray@gmail.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* chore(authProviders): drop cookieHeaderLen from OIDC diagnostics (E4 amendment)

Codex E4 amendment (optional, low-priority): hasCookieHeader is
sufficient to diagnose missing session-cookie issues; the
cookieHeaderLen byte count adds no operational value and is one more
PII-adjacent surface to keep an eye on. Drop it from both diagnostic
paths in pods/authProviders/src/openid.ts.

Signed-off-by: Michael Uray <michael.uray@gmail.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* auth(oidc): gate verbose callback diagnostics behind OIDC_DEBUG (L-AUTH-2)

Bei jedem fehlgeschlagenen OIDC-Callback wurde ein umfangreicher Diag-Block
(errStack/sessionKeys/host/…) auf error-Level geloggt -> anonymes Log-
Flooding via wiederholter invalider Callbacks. Normalbetrieb loggt jetzt nur
eine knappe Fehlerzeile; der volle Diag-Block nur mit OIDC_DEBUG=true.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* account(admin): drop non-email ADMIN_EMAILS entries unless explicitly allowed (L-AUTH-3)

Ein Tippfehler wie ADMIN_EMAILS=admin,michel@... legte bislang einen
unbeabsichtigten Admin-Identifier an (Eintrag ohne @ wurde still behalten).
Jetzt werden @-lose Eintraege per Default verworfen (fail-closed) und nur
bei ADMIN_EMAILS_ALLOW_LOGIN_ID=true als Login-ID akzeptiert; Warnung stets.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* auth(oidc): redirect to /login when provider auth yields no account (L-AUTH-4)

handleProviderAuth liefert '' bei einem nicht aufloesbaren Login (z.B. kein
Account + Signup disabled). Bisher erfolgte dann kein Redirect und next()
lief ins Leere -> haengende/leere Antwort. Jetzt fail-closed: explizites
302 auf /login?error=oidc_no_account.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* test(account): use example.com fixtures in adminEmails tests

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* auth(oidc): retry strategy registration with capped backoff (L-AUTH-5)

The account service registered the OIDC passport strategy only once at
startup via a single fire-and-forget Issuer.discover(). A transient issuer
outage in that one boot moment left the 'oidc' strategy permanently
unregistered, producing 500 "Unknown authentication strategy" on every
OpenID login until a manual container restart (observed: ~10 days).

Make registration self-healing:

- Extract registerOidcStrategyWithRetry(), which retries the ENTIRE
  registration chain (discover -> new Client -> new Strategy -> passport.use)
  with capped exponential backoff (5s, x2, cap 60s), unbounded. The loop only
  returns after passport.use succeeds, so a failure at client or strategy
  construction (e.g. temporarily incomplete issuer metadata during an IdP
  upgrade) is retried too instead of permanently disabling OIDC. discover and
  sleep are injectable for tests; passport is a structural { use } interface.
  It never rejects: the success log runs failure-safe AFTER passport.use and
  returns immediately, so a throwing logger can never re-enter the loop and
  register the strategy a second time; the failure warn is likewise guarded so
  a throwing sink cannot break the never-rejects contract.

- Gate log flooding via shouldWarnOnAttempt(): warn on the first 5 attempts
  and every 60th thereafter (~24 warn lines/day at the 60s cap instead of
  ~1440), following the branch's L-AUTH-2 rationale; error stacks only under
  OIDC_DEBUG. Success logs once with the attempt count.

- Wire it fire-and-forget in registerOpenid() and flip an oidcReady flag on
  success. The .then body is a plain boolean flip that cannot throw; a trailing
  .catch(() => {}) is belt-and-suspenders so this fire-and-forget chain can
  never surface an unhandled rejection. Routes stay registered synchronously.

- During the pending window, /auth/openid responds 503 with Retry-After: 5
  and a short text body instead of a 500 or a redirect (the login app ignores
  an error query param, and L-AUTH-4 only covers the callback path).

Add the package's first tests: the retry helper in isolation (schedule, 60s
cap, whole-attempt retry incl. client-construction failure, warn gating,
OIDC_DEBUG both ways, a throwing success log that must not re-register, and a
throwing failure warn that must not reject) and the registerOpenid wiring
(synchronous route registration, 503 pending window, exactly-once strategy
registration).

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* account(admin): keep non-email ADMIN_EMAILS entries by default; strict mode opt-in

Review feedback on #10931: dropping entries without "@" by default would
silently revoke admin rights on upgrade for deployments that use login-id
style values. The repo's own dev/tests/ws-tests compose files ship
ADMIN_EMAILS=admin,${PLATFORM_ADMIN_EMAILS} and tests/prepare-pg.sh creates
that `admin` account.

Restore the original semantics as the default: every non-empty entry is
kept (trimmed + lowercased), and entries without "@" are logged once at
startup so a typo stays visible. The fail-closed behaviour becomes an
explicit opt-in via ADMIN_EMAILS_STRICT=true. ADMIN_EMAILS_ALLOW_LOGIN_ID,
introduced earlier on this branch and never released, is removed so there
is a single switch.

Empty entries stay dropped. This is a deliberate tightening compared to
the original `new Set(ADMIN_EMAILS.split(','))`, which put '' into the set
for any empty entry (ADMIN_EMAILS='', ',admin', 'admin,', 'admin,,ops@...')
and so made isAdminEmail('') true. loginOrSignUpWithProvider passes '' when
the provider returns no email, so such a login received an admin token.
isAdminEmail('') is now always false.

Tests: default keeps `admin` and warns, strict drops, only the exact value
"true" enables strict mode, no warning for clean values, empty entries in
every position never make an empty email an admin, trim/lowercase coverage
unchanged. Each test saves, clears and restores ADMIN_EMAILS and
ADMIN_EMAILS_STRICT so none depends on the outer environment.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* auth(oidc): settle the passport callback wrapper on rejection; address review

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

* auth(providers): pass only provider-verified emails; abort OIDC retry on shutdown; more callback tests

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>

---------

Signed-off-by: Michael Uray <michael.uray@gmail.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Co-authored-by: Michael Uray <michaeluray@users.noreply.github.com>
2026-10-13 13:23:33 +07:00
2026-06-03 03:52:47 +05:00
2026-07-04 11:43:26 +07:00
2026-09-29 07:25:34 +02:00
2026-06-03 03:52:47 +05:00
2026-09-30 13:16:18 +02:00
2026-09-30 05:12:59 +02:00
2026-10-06 21:29:54 +07:00
2025-10-07 20:23:46 +05:00
2025-01-14 12:12:15 +07:00
2026-06-30 14:46:40 +05:00
2026-05-10 22:34:35 +05:00
2026-01-10 15:23:38 +05:00
2021-08-02 21:39:24 +02:00
2026-07-14 15:09:29 +07:00

Huly Platform

X (formerly Twitter) Follow GitHub License

⭐️ Your star shines on us. Star us on GitHub!

Important

This repository is frozen and is no longer actively maintained.

Development continues in Platform-Collective/platform. Please use the new repository for current source code, issues, and pull requests.

Important

Hosted Huly has shut down.

The hosted Huly service has been discontinued because its hosting is no longer funded. The hosted platform is no longer available.

You can continue using Huly with a self-hosted setup or another hosted option. For backup and restoration instructions, see the backup & restore guide.

Have questions or need help? Join the Huly community to discuss migration options and get support.

About

The Huly Platform is a robust framework designed to accelerate the development of business applications, such as CRM systems. This repository includes several applications, such as Chat, Project Management, CRM, HRM, and ATS. Various teams are building products on top of the Platform, including Huly and TraceX.

Huly

Self-Hosting

If you're primarily interested in self-hosting Huly without the intention to modify or contribute to its development, please use huly-selfhost. This project offers a convenient method to host Huly using docker, designed for ease of use and quick setup. Explore this option to effortlessly enjoy Huly on your own server.

Activity

Alt

API Client

If you want to interact with Huly programmatically, check out our API Client documentation. The API client provides a typed interface for all Huly operations and can be used to build integrations and custom applications.

You can find API usage examples in the Huly examples repository.

Changelog

For detailed information about changes, improvements, and bug fixes in each version, see our Changelog.

Versions

The Huly Platform uses two types of version tags to distinguish between production-ready and development releases:

  • Production Versions (v*) - Stable releases for end users

    • Example: v0.7.310, v0.7.307, v0.6.501
    • These versions are recommended for production deployments
    • Suitable for self-hosted installations
    • Published with release notes on GitHub Releases
  • Development Versions (s*) - Pre-release builds for developers

    • Example: s0.7.313, s0.7.292, s0.7.288
    • Used for development and testing purposes
    • May contain experimental features or bug fixes
    • Not recommended for production use

Architecture

For detailed information about the platform architecture, services, and their interactions, see our Architecture Overview.

Table of Contents

Pre-requisites

If you use nvm, run this after entering the repo to align your shell with the repository Node version:

nvm use

Verification

To verify the installation, perform the following checks in your terminal:

  • Ensure that the docker commands are available:
docker --version
docker compose version

Branches & Contributing

  • The main branch is the default branch used for production deployments. Changes to this branch are made from the staging branch once a version is ready for community use.

  • The staging branch is used for pre-release testing. It is stable enough for testing but not yet ready for production deployment.

  • The develop branch is used for development and is the default branch for contributions.

We periodically merge develop into staging to perform testing builds. Once we are satisfied with the build quality in our pre-release deployment, we merge changes into main and release a new version to the community.

Setup dev environment

To initialise the communication submodule

git submodule init
git submodule update

To update the communication submodule

git submodule update

Authentication

This project uses GitHub Packages for dependency management. To successfully download dependencies, you need to generate a GitHub personal access token and log in to npm using that token.

Follow these steps:

  1. Generate a GitHub Token:
  • Log in to your GitHub account
  • Go to Settings > Developer settings > Personal access tokens (https://github.com/settings/personal-access-tokens)
  • Click Generate new token
  • Select the required scopes (at least read:packages)
  • Generate the token and copy it
  1. Authenticate with npm:
npm login --registry=https://npm.pkg.github.com

When prompted, enter your GitHub username, use the generated token as your password

Fast start

sh ./scripts/fast-start.sh

Installation

You need Microsoft's rush to install the application.

  1. Install Rush globally using the command:
npm install -g @microsoft/rush
  1. Navigate to the repository root and run the following commands:
rush install
rush build

Alternatively, you can just execute:

sh ./scripts/presetup-rush.sh

Build and run

Development environment setup requires Docker to be installed on system.

Support is available for both amd64 and arm64 containers on Linux and macOS.

cd ./dev/
rush build    # Will build all the required packages.
# rush rebuild  # could be used to omit build cache.
rush bundle   # Will prepare bundles.
rush package  # Will build all webpack packages.
rush validate # Will validate all sources with typescript and generate d.ts files required for ts-node execution.
rush svelte-check # Optional. svelte files validation using svelte-check.
rush docker:build   # Will build Docker containers for all applications in the local Docker environment.
rush docker:up # Will set up all the containers

Be aware rush docker:build will automatically execute all required phases like build, bundle, package.

Note: For resource-constrained machines, you can use the minified variants rush docker:min and rush docker:up:min to build and run only the required services (excludes hulypulse, redis, process, backup, rating, preview, link-preview, elastic, fulltext, payment, stats, print, sign, hulygun, hulykvs).

Alternatively, you can just execute:

sh ./scripts/build.sh

By default, Docker volumes named dev_db, dev_elastic, and dev_files will be created for the MongoDB, Elasticsearch, and MinIO instances.

Add the following lines to your hosts file:

  • macOS / Linux: /etc/hosts
  • Windows: C:\Windows\System32\drivers\etc\hosts
127.0.0.1 huly.local
::1 huly.local

Accessing the URL http://huly.local:8087 will lead you to the app in development mode.

Limitations:

  • Local installation does not support sending emails, thus disabling functionalities such as password recovery and email notifications.

Run in development mode

Development mode allows for live reloading and a smoother development process.

cd dev/prod
rush validate
rushx dev-server

Then go to http://localhost:8080

Select "Sign up" on the right panel and click the "Sign up with password" link at the bottom. Enter the new user's credentials, then proceed to create a workspace for them.

Update project structure and database

If the project's structure is updated, it may be necessary to relink and rebuild the projects.

rush update
rush build

Troubleshooting

If a build fails, but the code is correct, try to delete the build cache and retry.

# from the project root
rm -rf common/temp/build-cache

Build & Watch

For development purpose rush build:watch action could be used.

It includes build and validate phases in watch mode.

Tests

Unit tests

rush test # To execute all tests

rushx test # For individual test execution inside a package directory

UI tests

cd ./tests
rush build
rush bundle
rush docker:build
## creates test Docker containers and sets up test database
./prepare.sh
## runs UI tests
rushx uitest

To execute tests in the development environment, please follow these steps:

cd ./tests
./create-local.sh ## use ./restore-local.sh if you only want to restore the workspace to a predefined initial state for sanity.
cd ./sanity
rushx dev-uitest # To execute all tests against the development environment.
rushx dev-debug -g 'pattern' # To execute tests in debug mode with only the matching test pattern.

Package publishing

node ./common/scripts/bump.js -p projectName

Additional testing

This project is tested with BrowserStack.

Native Windows build

The repository can be built and run directly on Windows, without WSL. Build and run scripts are written in bash; on Windows they are executed with Git Bash through common/scripts/bash.js, so no extra setup is needed beyond the usual prerequisites:

  • Git for Windows (it provides Git Bash). If it is installed in a non-standard location and git is not on PATH, set GIT_BASH to the full path of bash.exe, e.g. C:\Program Files\Git\bin\bash.exe.
  • Node.js, Rush and Docker Desktop as described in Pre-requisites.

Run rush and rushx commands from any shell (PowerShell, cmd or Git Bash).

Notes for contributors:

  • Do not put bash syntax ($(...), rm -rf, cp, || true, >/dev/null, single quotes) directly into package.json scripts: Windows runs them with cmd.exe. Put such a command into the bashScripts section of the same package.json and call it as node <path-to-root>/common/scripts/bash.js --script <name>.
  • Call .sh files as node <path-to-root>/common/scripts/bash.js <path-to-script>.sh [args], not directly.
  • .sh files are always checked out with LF line endings (see .gitattributes).
  • On Windows the wrapper disables Git Bash path conversion (MSYS_NO_PATHCONV=1), so arguments such as UPLOAD_URL=/files are passed as is.

WSL build guide

This guide describes the nuances of building and running the application from source code located on your NTFS drive, which is accessible from both Windows and WSL.

Prerequisites

Disk Space Requirements

Ensure you have sufficient disk space available:

  • A fully deployed local application in clean Docker will consume slightly more than 35 GB of WSL virtual disk space
  • The application folder after build (sources + artifacts) will occupy 4.5 GB

If there's insufficient space on your system drive (usually C:\), you can change the virtual disk location in Docker Settings → Resources → Advanced.

Docker WSL Integration

Make sure Docker is accessible from WSL:

  1. Go to Docker Settings → Resources → Advanced → WSL Integration
  2. Select the distribution where you'll be building and running the application
  3. Verify integration works by running this command in WSL:
    docker run hello-world
    

Common Issues and Solutions

Git Line Endings on Windows

Windows Git often automatically replaces line endings. Since most build scripts are .sh files, ensure your Windows checkout doesn't break them.

Solution options:

  • Checkout from WSL instead of Windows
  • Configure Git on Windows to disable auto-replacement:
    git config --global core.autocrlf false
    
    This disables auto-replacement for all repositories on your machine.

Elevated Privileges in WSL

Some commands in the instructions require elevated privileges when working in WSL. If you're using Ubuntu distribution, prefix commands with sudo:

sudo npm install -g @microsoft/rush

WSL Configuration

If the source code is located on a Windows NTFS drive, then edit the /etc/wsl.conf file in WSL (e.g., sudo nano /etc/wsl.conf) and add the following content if it doesn't exist:

[automount]
enabled = true
root = /mnt/
options = "metadata,umask=22,fmask=11"

[interop]
appendWindowsPath = false

However, we recommend storing the repository on a WSL disk, as this dramatically improves build and maintenance operations.

Running the Application

After these preparations, the build instructions should work without issues.

Port Conflicts

When starting the application (rush docker:up), some network ports in Windows might be occupied. You can fix port mapping in the \dev\docker-compose.yaml file.

Important: Depending on which port you change, you'll need to:

  1. Find what's using that port
  2. Update the new address in the corresponding service configuration

© 2025 Hardcore Engineering Inc.

Languages
TypeScript 51.3%
Svelte 28%
JSON-with-Comments 16.4%
JavaScript 1.6%
SCSS 0.9%
Other 1.6%