README.md

# TabletAuth

A secure authentication system designed for tablet and mobile device applications requiring simple PIN-based access with device registration capabilities.

## Features

- **PIN-based Authentication**: Secure numeric PIN authentication with configurable length
- **Device Registration**: Register mobile devices to user accounts via HTTPS API
- **Account Security**: Built-in protection against brute force attacks with account lockout
- **PIN Strength Validation**: Prevents weak PINs (sequential, repetitive, common patterns)
- **Session Management**: Track user activity and session validity
- **Flexible Integration**: Designed to work with any Ecto-compatible database

## Installation

Add `tablet_auth` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:tablet_auth, "~> 0.1.0"}
  ]
end
```

## Usage

TabletAuth supports two registration patterns:

1. **Tablet User Authentication** - Direct PIN authentication for tablet users
2. **Display-Based Registration** - PIN-based tablet registration using display entities (recommended for family photo sharing apps)

### Display-Based Tablet Registration (Recommended)

This is the primary approach for applications that manage displays/frames that can have tablets registered to them.

#### Generating a Registration PIN

```elixir
# Generate a PIN for a display that tablets can use to register
case TabletAuth.generate_display_pin(display, repo: MyApp.Repo) do
  {:ok, updated_display} -> 
    # Display now has registration_pin and pin_expires_at set
    # Share this PIN with users to register their tablets
  {:error, changeset} -> 
    # Handle validation errors
end
```

#### Registering a Tablet with PIN

```elixir
attrs = %{friendly_name: "Kitchen Tablet"}

opts = [
  repo: MyApp.Repo,
  display_schema: MyApp.Display,  # Your display schema (optional if using default)
  user_display_schema: MyApp.UsersDisplays,  # Association schema (optional)
  post_schema: MyApp.Post,  # For finding best parent display (optional)
  pubsub_module: Phoenix.PubSub,  # For broadcasting updates (optional)
  broadcast_topic: "user_settings:{owner_id}"  # Topic pattern (optional)
]

case TabletAuth.register_tablet_with_pin("123456", attrs, opts) do
  {:ok, %{tablet: tablet, association: association}} -> 
    # Tablet registered successfully
    # tablet.secret_key can be used for tablet access
  {:error, :invalid_or_expired_pin} -> 
    # Wrong or expired PIN
  {:error, changeset} -> 
    # Handle registration failure
end
```

#### Looking Up Tablets

```elixir
# Validate a PIN (useful for API endpoints)
case TabletAuth.validate_pin("123456", repo: MyApp.Repo) do
  %Display{} = display -> 
    # PIN is valid, display can be used for registration
  nil -> 
    # Invalid or expired PIN
end

# Get tablet by secret key (for tablet access)
case TabletAuth.get_tablet_by_secret(secret_key, repo: MyApp.Repo) do
  %Display{} = tablet -> 
    # Tablet found
  nil -> 
    # Tablet not found
end
```

### Creating a Tablet User

```elixir
attrs = %{
  name: "John Doe",
  pin: "1357"
}

opts = [
  pin_length: 4,
  max_attempts: 3,
  lockout_duration: 15  # minutes
]

case TabletAuth.create_tablet_user(attrs, opts) do
  {:ok, changeset} -> 
    # Save changeset to your database
    MyApp.Repo.insert(changeset)
  {:error, :weak_pin} -> 
    # Handle weak PIN error
  {:error, changeset} -> 
    # Handle validation errors
end
```

### Authenticating with PIN

```elixir
# For tablet authentication (finds active tablet user)
case TabletAuth.authenticate_pin("1357") do
  {:ok, user} -> 
    # Authentication successful
  {:error, :invalid_pin} -> 
    # Wrong PIN
  {:error, :account_locked} -> 
    # Too many failed attempts
end

# For device authentication
case TabletAuth.authenticate_device("1357", "device_123") do
  {:ok, user} -> 
    # Device authentication successful
  {:error, reason} -> 
    # Handle authentication failure
end
```

### Device Registration

```elixir
device_attrs = %{
  device_id: "mobile_device_abc123",
  device_name: "John's iPhone"
}

user_lookup = %{
  email: "john@example.com"
  # or user_id: "user_123"
  # or external_user_id: "external_456"
}

case TabletAuth.register_device(device_attrs, user_lookup) do
  {:ok, registration} -> 
    # Device registered successfully
  {:error, :device_already_registered} -> 
    # Device is already registered
  {:error, reason} -> 
    # Handle registration failure
end
```

### Session Management

```elixir
# Update user activity
TabletAuth.update_last_activity(user_id)

# Check if session is valid
if TabletAuth.session_valid?(user_id, session_timeout_minutes: 60) do
  # Session is still active
else
  # Session expired, require re-authentication
end

# Revoke device access
case TabletAuth.revoke_device("device_123") do
  {:ok, revocation} -> 
    # Device access revoked
  {:error, reason} -> 
    # Handle revocation failure
end
```

## Database Schema

### For Display-Based Registration

Create a migration for the displays table:

```elixir
defmodule MyApp.Repo.Migrations.CreateDisplays do
  use Ecto.Migration

  def change do
    create table(:displays, primary_key: false) do
      add :id, :binary_id, primary_key: true
      add :title, :string, null: false
      add :registration_pin, :string
      add :pin_expires_at, :utc_datetime
      add :secret_key, :string
      add :friendly_name, :string
      add :is_tablet, :boolean, default: false
      add :tablet_parent_display, :binary_id
      add :owner_id, :binary_id, null: false
      
      timestamps(type: :utc_datetime)
    end
    
    create index(:displays, [:owner_id])
    create index(:displays, [:registration_pin])
    create index(:displays, [:secret_key])
    create index(:displays, [:is_tablet])
    create index(:displays, [:tablet_parent_display])
  end
end
```

### For Tablet User Authentication

Create a migration for the tablet users table:

```elixir
defmodule MyApp.Repo.Migrations.CreateTabletUsers do
  use Ecto.Migration

  def change do
    create table(:tablet_users, primary_key: false) do
      add :id, :binary_id, primary_key: true
      add :name, :string, null: false
      add :pin_hash, :string, null: false
      add :active, :boolean, default: true
      add :last_activity, :utc_datetime
      add :failed_attempts, :integer, default: 0
      add :locked_until, :utc_datetime
      add :device_id, :string
      add :device_name, :string
      add :registered_at, :utc_datetime
      add :external_user_id, :string
      
      timestamps()
    end
    
    create unique_index(:tablet_users, [:device_id])
    create index(:tablet_users, [:external_user_id])
    create index(:tablet_users, [:active])
  end
end
```

## Security Features

### PIN Strength Validation

The system automatically rejects weak PINs including:
- All same digits (0000, 1111, etc.)
- Sequential patterns (1234, 4321, etc.)
- Common weak patterns
- Repetitive patterns (more than half digits the same)

### Account Security

- **Failed Attempt Tracking**: Counts consecutive failed login attempts
- **Account Lockout**: Temporarily locks accounts after too many failures
- **Secure Hashing**: Uses bcrypt with 12 rounds for PIN storage
- **No Plaintext Storage**: PINs are never stored in plaintext

### Session Security

- **Activity Tracking**: Updates last activity timestamp on successful auth
- **Session Timeouts**: Configurable session timeout validation
- **Device Revocation**: Ability to remotely revoke device access

## Configuration Options

All functions accept an `opts` keyword list for configuration:

- `:pin_length` - Required PIN length (default: 4)
- `:max_attempts` - Maximum failed attempts before lockout (default: 3)
- `:lockout_duration` - Lockout duration in minutes (default: 15)
- `:session_timeout_minutes` - Session timeout in minutes (default: 60)
- `:repo` - Ecto repo module for database operations

## Integration with Your App

This library is designed to be framework-agnostic. You'll need to:

1. **Add the schema to your app** and customize fields as needed
2. **Implement the database operations** in your context modules
3. **Add API endpoints** for device registration and authentication
4. **Configure security settings** based on your requirements

## Security Considerations

- Always use HTTPS for device registration and authentication APIs
- Implement rate limiting on authentication endpoints
- Consider additional security measures for high-value applications
- Regularly review and update PIN strength requirements
- Monitor failed authentication attempts for suspicious activity

## License

MIT License. See LICENSE file for details.