> ## Documentation Index
> Fetch the complete documentation index at: https://globaliptv.misidev.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Credential-based login, auto-login, session re-auth, and failure classification for GlobalTV Roku.

## Overview

Authentication in GlobalTV Roku is credential-based. The user enters a username and password on the `LoginScreen`, and `AuthTask` calls the backend to validate them. On success, credentials are saved to the Roku Registry and used for auto-login on the next launch. A background session check re-authenticates at a fixed interval while the app is running.

<CardGroup cols={2}>
  <Card title="AuthTask" icon="bolt">
    Background task that performs the HTTP auth request and writes result fields
  </Card>

  <Card title="RegistryManager" icon="database">
    Persists and retrieves credentials under the `GlobalTV` registry section
  </Card>

  <Card title="LoginScreen" icon="user">
    Captures username and password using Roku's on-device keyboard dialog
  </Card>

  <Card title="GTV_AuthClassifyFailure" icon="triangle-exclamation">
    Classifies every auth failure into one of five reason codes
  </Card>
</CardGroup>

***

## Auth flow

<Steps>
  <Step title="User enters credentials">
    `LoginScreen` opens a `StandardKeyboardDialog` for the username field, then another for the password field. On submit, it calls `TryLogin()`, which first requests the Roku email via `ChannelStore.getUserData` (RFI), then starts `AuthTask`.
  </Step>

  <Step title="Server resolution">
    `AuthTask` calls `GTV_ResolveServerForAuth()`, which iterates the server probe list — LAN servers first, then the public WAN server — using health-check pings. The first server that responds becomes the active server for this session and is saved to the Registry (`lastServer` key).
  </Step>

  <Step title="Auth HTTP request">
    `AuthTask` issues a GET to the auth endpoint with URL-encoded credentials:

    ```brightscript theme={null}
    authUrl = server + "/auth/" + encUser + "/" + encPass
    resp = GTV_HttpGet(authUrl, c.TIMEOUT_AUTH)
    ```

    The endpoint pattern (from `AppConstants.PATH_AUTH_TPL`) is:

    ```text theme={null}
    /auth/{user}/{pass}
    ```
  </Step>

  <Step title="Response parsing">
    On HTTP 2xx, the task parses the JSON body and checks the `subscriberStatus`, `subscriber_active`, or `active` fields to confirm the subscriber is active. If any field signals an inactive account, the task classifies the failure and exits without saving credentials.
  </Step>

  <Step title="Success: credentials saved">
    On a fully successful auth, `AuthTask` saves credentials to the Registry and sets global state:

    ```brightscript theme={null}
    GTV_RegSaveCredentials(username, password)  ' saves username + password keys
    m.global.activeServer     = server
    m.global.isAuthenticated  = true
    m.global.subscriberActive = true
    ```
  </Step>

  <Step title="LoginScreen receives result">
    `LoginScreen` observes `AuthTask.done`. On success, it emits `loginSucceeded = true` with `loginResult` (username, password, rokuEmail). `MainScene` handles the rest of the launch sequence.
  </Step>
</Steps>

***

## Auth endpoint

The endpoint template is defined in `AppConstants` as `PATH_AUTH_TPL`:

```brightscript theme={null}
PATH_AUTH_TPL : "/auth/{user}/{pass}"
```

At runtime, `AuthTask` builds the URL by URL-encoding both values:

```brightscript theme={null}
encUser = GTV_UrlEncode(username)
encPass = GTV_UrlEncode(password)
authUrl = server + "/auth/" + encUser + "/" + encPass
```

***

## Auto-login

On every app launch, `MainScene` checks whether credentials exist in the Registry before showing the `LoginScreen`:

```brightscript theme={null}
' RegistryManager.brs
function GTV_RegHasCredentials() as Boolean
    c = AppConstants()
    u = GTV_RegRead(c.REG_KEY_USER)
    p = GTV_RegRead(c.REG_KEY_PASS)
    return (u <> "" and p <> "")
end function

function GTV_RegLoadCredentials() as Object
    c = AppConstants()
    u = GTV_RegRead(c.REG_KEY_USER)
    p = GTV_RegRead(c.REG_KEY_PASS)
    if u = "" or p = ""
        return invalid
    end if
    return {username: u, password: p}
end function
```

If credentials are found, `MainScene` skips the `LoginScreen` and starts `AuthTask` directly with the saved values.

***

## Registry keys

All registry values are stored under the `GlobalTV` section (`REG_SECTION = "GlobalTV"`).

| Constant          | Key string         | Purpose                                |
| ----------------- | ------------------ | -------------------------------------- |
| `REG_KEY_USER`    | `username`         | Saved username                         |
| `REG_KEY_PASS`    | `password`         | Saved password                         |
| `REG_KEY_SERVER`  | `lastServer`       | Last resolved server URL               |
| `REG_KEY_CHANNEL` | `lastChannelIndex` | Last viewed channel index (for resume) |

***

## Session re-auth check

After a successful launch, `MainScene` runs a periodic re-auth check to detect expired or revoked sessions while the app is in use.

```brightscript theme={null}
SESSION_AUTH_CHECK_MS : 420000   ' 7 minutes
```

If re-auth fails with a credential or inactive reason code, the user is sent back to the `LoginScreen` with a pre-populated error notice.

***

## Timeouts and retries

| Constant                   | Value      | Description                                            |
| -------------------------- | ---------- | ------------------------------------------------------ |
| `TIMEOUT_AUTH`             | `15000` ms | Maximum wait time for the auth HTTP response           |
| `TIMEOUT_HTTP`             | `12000` ms | General HTTP timeout used for non-auth requests        |
| `TIMEOUT_HEALTH`           | `2500` ms  | Server ping timeout during server resolution           |
| `RETRY_MAX`                | `3`        | Maximum retry attempts for network-level failures      |
| `SERVER_LAN_FORCE_RETRIES` | `3`        | Number of LAN probe retries before failing over to WAN |

The `LoginScreen` also arms a guard timer to cancel a hung auth attempt. The guard duration is computed as:

```brightscript theme={null}
function GTV_LoginGuardDurationSeconds() as Integer
    c = AppConstants()
    totalMs = (c.TIMEOUT_HEALTH * (c.SERVER_LAN_FORCE_RETRIES + 1)) + c.TIMEOUT_AUTH + 5000
    if totalMs < 20000 then totalMs = 20000
    return Int((totalMs + 999) / 1000)
end function
```

***

## Failure classification

`GTV_AuthClassifyFailure` maps every auth error to one of five integer reason codes. Both `AuthTask` and `PlaylistTask` call this function to decide how to present errors to the user and whether to clear credentials.

### Reason codes

| Constant                       | Value | Meaning                                           |
| ------------------------------ | ----- | ------------------------------------------------- |
| `AUTH_REASON_NONE`             | `0`   | No failure — auth succeeded                       |
| `AUTH_REASON_NETWORK_DOWN`     | `460` | Network unavailable or HTTP timeout (`code = -1`) |
| `AUTH_REASON_CREDENTIALS`      | `401` | Wrong username or password                        |
| `AUTH_REASON_INACTIVE`         | `470` | Account exists but subscriber is inactive         |
| `AUTH_REASON_PASSWORD_CHANGED` | `471` | Password was changed or session was revoked       |

### Function signature

```brightscript theme={null}
function GTV_AuthClassifyFailure(
    httpCode      as Integer,
    reasonText    = invalid as Dynamic,
    bodyText      = invalid as Dynamic,
    subscriberActive = invalid as Dynamic
) as Integer
```

**Parameters:**

* `httpCode` — The HTTP response code. `-1` immediately returns `AUTH_REASON_NETWORK_DOWN`.
* `reasonText` — A string hint from the response (e.g. the `reason` or `message` field). May be `invalid`.
* `bodyText` — The raw response body string or parsed object. The function extracts signal text from nested JSON keys (`message`, `reason`, `error`, `detail`, `status`, `user_inactive_reason`, `subscriberDisabledReason`).
* `subscriberActive` — Boolean hint from a parsed subscriber status field. If explicitly `false`, returns `AUTH_REASON_INACTIVE`.

### Classification logic

```brightscript theme={null}
function GTV_AuthClassifyFailure(httpCode as Integer, reasonText = invalid as Dynamic, bodyText = invalid as Dynamic, subscriberActive = invalid as Dynamic) as Integer
    c = AppConstants()

    if httpCode = -1
        return c.AUTH_REASON_NETWORK_DOWN
    end if

    signal = GTV_AuthToText(reasonText)
    signal = GTV_AuthConcatSignal(signal, GTV_AuthToText(bodyText))
    signal = GTV_AuthConcatSignal(signal, GTV_AuthExtractSignalFromBody(bodyText))
    signal = GTV_AuthNormalizeSignal(signal)

    if GTV_AuthLooksInactive(signal, subscriberActive)
        return c.AUTH_REASON_INACTIVE
    end if

    if GTV_AuthLooksCredentialFailure(signal)
        return c.AUTH_REASON_CREDENTIALS
    end if

    if GTV_AuthLooksPasswordChanged(signal)
        return c.AUTH_REASON_PASSWORD_CHANGED
    end if

    if httpCode = 401 or httpCode = 403 or httpCode = 404
        return c.AUTH_REASON_CREDENTIALS
    end if

    return c.AUTH_REASON_NONE
end function
```

<Note>
  Signal text is Unicode-normalized (accented characters stripped) and lowercased before pattern matching, so Spanish and English error messages from the backend are handled identically.
</Note>

### How `LoginScreen` reacts to each code

| Code                            | UI behavior                                                              |
| ------------------------------- | ------------------------------------------------------------------------ |
| `AUTH_REASON_CREDENTIALS`       | Clears the password field, focuses password input, shows error message   |
| `AUTH_REASON_PASSWORD_CHANGED`  | Clears the password field, focuses password input, shows error message   |
| `AUTH_REASON_INACTIVE`          | Clears the password field, focuses the login button, shows error message |
| `AUTH_REASON_NETWORK_DOWN`      | Shows the server timeout error, focuses the login button                 |
| `AUTH_REASON_NONE` (unexpected) | Shows generic error, focuses the login button                            |
