Skip to content

Authentication v1.0.0

Source: src/services/authentication.service.ts

The identity service extends IssuerApplication — it signs JWTs with ES256 (ECDSA P-256) via JWKSIssuerTokenService and publishes the public key set at /jw-certs. All other services extend VerifierApplication and verify tokens by fetching this public key.

Dependencies

Sign-In

Flow

Validation Steps

StepCheckError
1UserIdentifier exists by (scheme, identifier)404 — identifier not found
2userIdentifier.verified === true403 — not verified
3User exists with status: ACTIVATED404 — user not found
4User has credential for the scheme403 — no credentials
5Bun.password.verify() matches401 — invalid credential

Only BASIC (password) credential scheme is implemented. Others (2fa, oauth, oauth2) throw 400 Unsupported credential scheme.

JWT Token Payload

FieldTypeSource
userIdstringUser Snowflake ID
rolesArray<{id, identifier, priority}>PolicyDefinition GROUP (USER→ROLE)
clientIdstringFrom sign-in request (optional)
organizerIdsstringComma-separated, from PolicyDefinition GROUP (USER→ORGANIZER)
merchantIdsstringComma-separated, from PolicyDefinition GROUP (USER→MERCHANT)
iat / expnumberAdded by IGNIS

Response

typescript
{
  userId: string,
  roles: Array<{ id: string, identifier: string, priority: number }>,
  token: { value: string, type: 'Bearer' }
}

Sign-Up

Flow

Key Behaviors

  • All sign-up users → status: ACTIVATED + OWNER role (priority 500, identifier 500_organizer-owner). (Marked as TODO — temporary default)
  • Email verification on sign-up is commented out — not sent currently.
  • The sign-up flow does not emit any event (no WebSocket broadcast, no queue enqueue). The client must explicitly call the Commerce onboarding endpoint (POST /organizers/on-boarding) after sign-up to create the merchant hierarchy.

Request Schema

typescript
SignUpRequest {
  username:   string    // 4–80 chars, required
  credential: string    // 4–80 chars, must match PASSWORD_PATTERN, required
  emails:     string[]  // min 1, valid email format
  phones:     string[]  // min 1, E.164 format
  firstName:  string    // 1–255 chars
  lastName:   string    // 1–255 chars
  birthday?:  string    // ISO date
  locale?:    string
}

Change Password

RuleDetail
Self-onlycurrentUser.userId must match request userId (admin bypass is TODO)
Old password checkBun.password.verify() against stored hash
New password hashBun.password.hash() (Argon2id)
Error codes404 credential not found, 401 old password wrong, 401 unauthorized

Request Schema

typescript
ChangePasswordRequest {
  scheme:        string  // e.g., 'basic'
  oldCredential: string  // 4–80 chars, PASSWORD_PATTERN
  newCredential: string  // 4–80 chars, PASSWORD_PATTERN
  userId:        string
}

Identifier Schemes

Source: src/common/constants.tsUserIdentifierSchemes

SchemeValueAuto-VerifiedCan Sign-In
USERNAMEusernameYesYes
EMAILemailNoAfter verification
PHONE_NUMBERphone_numberNoAfter verification
NX_AUTHnx_authInternal use
USER_NUMBERuser_numberInternal use

Credential Schemes

SchemeValueStatus
BASICbasicImplementedBun.password (Argon2id)
TWO_FA2faDefined only
OAUTHoauthDefined only
OAUTH2oauth2Defined only

Validation Rules

Source: src/common/validations.ts

RuleValue
Username length4–80 characters
Password length4–80 characters
Password pattern^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$ (1 upper + 1 lower + 1 digit)
Phone format^\+?[1-9]\d{1,14}$ (E.164)
String length1–255 characters

Auth Endpoints

MethodPathAuthRequestResponse
POST/auth/sign-inSignInRequestSignInResponse
POST/auth/sign-upSignUpRequestSignUpResponse
POST/auth/change-passwordJWTChangePasswordRequest{ message }
GET/auth/user-informationJWT{} (placeholder)

JWKS Flow (Cross-Service)

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.