#591·AiToEarn

Uses Public Default Authentication Secrets

Author: 28HusCreated Sep 7, 2026Updated Sep 7, 2026
Labelsbug

Self Checks

  • I have read the Contributing Guide.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I can provide the report in English if needed to help more contributors participate in the discussion.
  • Please do not modify this template :) and fill in all the required fields.

Aitoearn version

Commit d3aa8bea5b146a8675607cf0144d891aad3e9683 and earlier versions

Please select your platform

Mac

Steps to reproduce

AiToEarn Uses Public Default Authentication Secrets

Package

AiToEarn

Affected versions

Commit d3aa8bea5b146a8675607cf0144d891aad3e9683 and earlier versions containing the affected code.

Patched versions

Unknown.

Description

Summary

AiToEarn contains public default values for authentication secrets. The Electron server uses a static fallback for AUTH_SECRET to sign and verify JWTs. The JWT payload contains isManager, and ManagerGuard grants manager access when this claim is true.

The backend configuration also contains a static internalToken. The shared authentication guard accepts this value directly as a Bearer token without JWT verification or user lookup. Internal controllers protected by @Internal() therefore rely on this publicly known value as their only authentication factor.

Details

Component File Issue
Electron server project/aitoearn-electron/server/src/auth/auth.module.ts Static fallback for AUTH_SECRET
Electron server project/aitoearn-electron/server/src/auth/manager.guard.ts Trusts isManager after JWT verification
Backend services project/aitoearn-backend/apps/aitoearn-server/config/config.yaml Static auth.secret and auth.internalToken defaults
Shared backend auth project/aitoearn-backend/libs/aitoearn-auth/src/aitoearn-auth.guard.ts Accepts internalToken directly

Vulnerable Code

project/aitoearn-electron/server/src/auth/auth.module.ts:

typescript
JwtModule.register({
  global: true,
  secret: process.env.AUTH_SECRET || "<public static default redacted>",
  signOptions: { expiresIn: '30d' },
})

The application places the manager flag inside the JWT:

typescript
const payload: TokenInfo = {
  phone: tokenInfo.phone,
  id: tokenInfo.id,
  name: tokenInfo.name,
  isManager: tokenInfo.isManager,
};
return this.jwtService.sign(payload);

ManagerGuard then accepts the authorization decision from the token:

typescript
const payload = await this.jwtService.verifyAsync(token, {
  secret: process.env.AUTH_SECRET,
});

if (!payload.isManager) {
  throw new UnauthorizedException();
}

The backend configuration also publishes static authentication values:

yaml
auth:
  secret: <public static default redacted>
  internalToken: <public static default redacted>

The shared guard accepts the internal token without establishing a user:

typescript
if (token === this.options.internalToken) {
  return true;
}

PoC

Run only against a local test deployment.

JWT manager authorization

  1. Leave AUTH_SECRET unset in the Electron server.
  2. Create a JWT with the public fallback key and a payload containing isManager: true.
  3. Send the token as Authorization: Bearer <token> to a route protected by @Manager(), such as the manager creation route.
  4. Repeat the test with a unique random AUTH_SECRET.

Example token-generation pseudocode:

javascript
const token = jwt.sign(
  { id: '<test-id>', isManager: true },
  '<public static default redacted>',
  { expiresIn: '30d' },
);

Expected secure result: the forged token is rejected. The affected result is that the request passes ManagerGuard and reaches the manager-only handler.

Internal bearer token

  1. Start the backend with the checked-in internalToken value unchanged.
  2. Send that value as a Bearer token to an endpoint protected by @Internal().
  3. For example, test the AI draft-generation internal endpoints using a body that satisfies the endpoint DTO.
  4. Repeat with a newly generated internal token.

Expected secure result: an external request without an explicitly authorized service credential is rejected. The affected result is that the static token passes the guard without JWT verification, API-key resolution, or user lookup.

Attack Scenario

  • An attacker reads the public repository and obtains the default values.
  • The deployment does not override the corresponding environment/configuration values.
  • The attacker signs a JWT with isManager: true, or sends the default internalToken as a Bearer token.
  • The server accepts the token as an authenticated manager or internal caller.

Impact

The Electron JWT fallback may allow unauthorized access to manager-only handlers, including manager administration operations. The backend internalToken may allow unauthorized access to internal service endpoints, including operations that accept a caller-supplied userId.

The backend auth.secret path additionally uses the token id to resolve an open user record. Its exact impact should be verified against the deployed token-issuing path, but the checked-in value is not suitable as a production JWT secret.

Recommended Fix

  1. Remove all static authentication fallbacks from source-controlled configuration.
  2. Fail closed when AUTH_SECRET, auth.secret, or internalToken is absent or unchanged from a development value.
  3. Generate instance-specific cryptographically random secrets during secure deployment and store them outside the repository.
  4. Replace the exposed values and invalidate tokens issued with them.
  5. Do not use a bearer string as an implicit internal identity; authenticate service callers with scoped, rotatable credentials and enforce service-side authorization.
  6. Add regression tests proving that default or missing secrets cannot start a production deployment and that forged manager/internal requests are rejected.

References

✔️ Expected Behavior

...

❌ Actual Behavior

...