# Changelog

## 1.0.74

Adds the multi-factor authentication surface introduced by DeepAffex API
release 2.92.0 (V2API-3490). TOTP through an authenticator app is the only
channel the API supports.

### Breaking (types only)

- The body of `users.login`, `organizations.login` and
  `users.loginWithPhoneCode` is now a three-way union rather than
  `{ Token, RefreshToken }`, because the API can answer a login with an MFA
  challenge or with a limited "pending" token instead of a token pair. Code
  that reads `body.Token` or `body.RefreshToken` off the result no longer
  compiles until the body is narrowed. Runtime behaviour is unchanged for
  logins that do not involve MFA.

  ```ts
  import client, { isMFAChallenge, isMFAPending, MFAChannel } from '@nuralogix.ai/dfx-api-client';

  const { status, body } = await api.http.organizations.login(data, true);
  if (status !== '200') return;

  if (isMFAChallenge(body)) {
    // enrolled: prompt for the authenticator code
    api.setSession({ userToken: body.MFAChallengeToken });
    await api.http.users.verifyMfa({ Code, MFAChallengeToken: body.MFAChallengeToken }, true);
  } else if (isMFAPending(body)) {
    // enrolment required: render the QR code, then verify
    api.setSession({ userToken: body.Token });
    const { body: setup } = await api.http.users.setupMfa({ Channel: MFAChannel.TOTP });
    await api.http.users.verifyMfa({ Code }, true);
  } else {
    // ordinary login: body.Token and body.RefreshToken are both present
  }
  ```

  The three login methods only populate the session for the ordinary outcome.
  For the challenge and pending outcomes the session is left untouched, and
  `users.verifyMfa` populates it once MFA completes.

### Added

- `users.setupMfa` - `POST /users/mfa/setup`. Stages a TOTP enrolment and
  returns the `bindingUri` to render as a QR code.
- `users.challengeMfa` - `POST /users/mfa/challenge`. A no-op success on TOTP.
- `users.verifyMfa` - `POST /users/mfa/verify`. Submits the code and returns a
  full token pair with the claim `MFAStatus: 'verified'`. Honours
  `x-nura-session` through its `enableSession` argument, exactly as
  `organizations.login` does, so a token minted by completing MFA can be
  session bound the same way.
- `users.getMfaStatus` - `GET /users/mfa/status`. Returns
  `{ channel, enrolled, verified, required }`.
- `users.deleteMfa` - `DELETE /users/mfa`. Self-service removal, taking the
  current `Password` (omitted for accounts that cannot log in with a password).
- `users.deleteMfaForUser` - `DELETE /users/:userId/mfa`. Admin removal,
  requiring MFA_ADMIN.
- `MFAErrorCode` and `isMFAError`, so the new error codes - `MFA_PENDING` (403),
  `INVALID_MFA_CODE` (401), `MFA_ENROLLMENT_EXPIRED` (410),
  `MFA_ALREADY_ENROLLED` (403), `INVALID_PASSWORD` (401),
  `MFA_REQUIRED_BY_POLICY` (403) and `ERR_SESSION_EXPIRED` (401) - can be
  branched on instead of matching message strings. `MFA_PENDING` and
  `ERR_SESSION_EXPIRED` are now reachable from `auths.renew` and
  `auths.generateToken`, which previously could not fail in those ways.
- `MFAPolicy` on the `organizations.retrieve` response and on the full-token and
  pending login envelopes. The API scopes it to the caller's own role, so it is
  a single-key object or `{}`, never the full multi-role policy.
- `MFAFullPolicy` on the `organizationsPrivate.update` payload and the
  `organizationsPrivate.retrieve` response, which are unscoped administration
  paths.
- `ValidationError`, `ValidationErrorResponse` and `isValidationError`, for the
  `400 VALIDATION_ERROR` body the API returns when a field fails validation. It
  carries per-field detail in `Errors` and an **empty** `Message`, so anything
  user facing has to be built from `Errors`. Not MFA specific, but the MFA
  endpoints are where clients meet it most, since `Channel`, `Code` and
  `MFAChallengeToken` are all validated.
- `MFATokenClaims` and `TokenClaims`, covering the four MFA claims
  (`MFAStatus`, `MFAChannel`, `MFAEnrolled`, `MFARequired`) that every token now
  carries. This package does not decode tokens; the types are for consumers that
  decode the payload themselves.
- `MFAChannel` and `MFAStatus` enums, and the `isMFAChallenge`, `isMFAPending`
  and `isFullTokenPair` narrowing helpers. All are also on the `enums` export.
- HTTP `410` is now part of the response status union, since
  `MFA_ENROLLMENT_EXPIRED` is returned with it.

### Deprecated

Nothing is removed, but four methods now point at removed or renamed API
routes:

- `users.createTwoFactorAuthSecret` - `/users/mfa/secret` no longer exists. Use
  `users.setupMfa`.
- `users.enableTwoFactorAuthForLoggedInUser` - use `users.setupMfa` then
  `users.verifyMfa`.
- `users.disableTwoFactorAuthForLoggedInUser` - the route now requires
  `Password`, which this call does not send. Use `users.deleteMfa`.
- `users.disableTwoFactorAuthForSpecifiedUser` - use `users.deleteMfaForUser`.
- The inline `MFAToken` field on `UserLoginRequest` and `OrgLoginRequest` is
  ignored by the API.
