# Authentication

This document covers the authentication methods for Sling API specifications. For the basic structure, see [Structure](https://docs.slingdata.io/concepts/api-specs/structure).

Sling supports several authentication methods, configured under the `authentication` key.

> ⚠️ **Important:** It's best practice to use environment variables or secrets for sensitive values instead of hardcoding them.

## Managing Secrets and Environment Variables

Sling provides flexible ways to manage sensitive authentication data through environment variables and the `env.yaml` file. Here's how to configure them:

### Using the env.yaml File

The primary method for managing secrets is through the [`env.yaml`](https://docs.slingdata.io/sling-cli/environment) file located at `~/.sling/env.yaml`. When defining API connections, you can specify secrets that will be available to your API specs.

```yaml
# ~/.sling/env.yaml
connections:
  my_api:
    type: api
    spec: file:///path/to/my_api.spec.yaml
    secrets:
      api_key: "your-secret-api-key"
      client_id: "your-oauth-client-id"
      client_secret: "your-oauth-client-secret"
      username: "your-username"
      password: "your-password"
      
  github_api:
    type: api
    spec: github
    secrets:
      token: "ghp_xxxxxxxxxxxxxxxxxxxx"
      
  stripe_api:
    type: api
    spec: stripe
    secrets:
      api_key: "sk_test_xxxxxxxxxxxxxxxxxxxx"
```

You'll be able to access the secrets values via the `{secrets.var_name}` expression.

```yaml
# In your API spec
defaults:
  request:
    headers:
      Authorization: "Bearer {secrets.api_key}"
```

### Using Environment Variables

You can also provide secrets via environment variables. In your API spec, use the `{env.VARIABLE_NAME}` syntax:

```bash
# Set environment variables
export API_USERNAME="myuser"
export API_PASSWORD="mypassword"
```

```yaml
# In your API spec
authentication:
  type: "basic"
  username: "{env.API_USERNAME}"
  password: "{env.API_PASSWORD}"
```

## Authentication Methods

Now that you understand how to manage secrets, here are the different authentication methods you can configure:

### No Authentication

```yaml
# Simply omit the authentication block for APIs that don't require authentication
```

### Static Header Authentication

For APIs that require headers for authentication (e.g., API keys, bearer tokens):

```yaml
authentication:
  type: "static"
  headers:
    Authorization: "Bearer {secrets.api_token}"
```

This is ideal for:

* API keys passed in headers (e.g., `X-API-Key: your-key`)
* Bearer tokens (e.g., `Authorization: Bearer your-token`)
* Custom authentication headers
* Multiple authentication headers

**Examples:**

```yaml
# API Key in custom header
authentication:
  type: "static"
  headers:
    X-API-Key: "{secrets.api_key}"

# Bearer token
# we can omit `type: static` and only specify `headers`
authentication:
  headers:
    Authorization: "Bearer {secrets.access_token}"

# Multiple headers
authentication:
  headers:
    Authorization: "Bearer {secrets.access_token}"
    X-API-Key: "{secrets.api_key}"
    X-Tenant-ID: "{secrets.tenant_id}"
```

**Shorthand Syntax:**

When only `headers` are provided without any other authentication configuration, the type defaults to `static`:

```yaml
# This is equivalent to type: "static"
authentication:
  headers:
    Authorization: "Bearer {secrets.api_token}"
```

> 📝 **Note:** For dynamic token retrieval (e.g., login flow), use the `sequence` authentication type instead.

### Basic Auth

```yaml
authentication:
  type: "basic"
  username: "{secrets.username}"
  password: "{secrets.password}"
```

### OAuth2 Authentication

For OAuth2 flows, use the `oauth2` type. Sling supports three OAuth2 flows:

| Flow                 | Use Case                             | Interactive             |
| -------------------- | ------------------------------------ | ----------------------- |
| `client_credentials` | Server-to-server, machine-to-machine | No                      |
| `authorization_code` | User-facing apps, browser-based auth | Yes                     |
| `device_code`        | CLI tools, headless environments     | Yes (on another device) |

**Full Property Reference:**

```yaml
authentication:
  type: "oauth2"
  flow: "client_credentials"  # client_credentials|authorization_code|device_code
  client_id: "{secrets.client_id}"
  client_secret: "{secrets.client_secret}"
  authentication_url: "https://api.example.com/oauth/token"      # Token endpoint (required)
  authorization_url: "https://api.example.com/oauth/authorize"   # Auth endpoint (for authorization_code)
  device_auth_url: "https://api.example.com/oauth/device/code"   # Device auth endpoint (for device_code)
  redirect_uri: "http://localhost:8080/callback"                 # Redirect URI (for authorization_code)
  scopes:
    - "read:data"
    - "write:data"
```

> **Token Persistence:** Sling automatically stores OAuth tokens in `~/.sling/api/tokens/{connection_name}.json` and refreshes them when they expire. You don't need to manage token refresh manually.

> **PKCE Support:** For public clients (when `client_secret` is empty), Sling automatically enables PKCE (Proof Key for Code Exchange) for added security.

**Client Credentials Flow (Server-to-Server):**

The most common flow for automated data pipelines. No user interaction required.

```yaml
authentication:
  type: "oauth2"
  flow: "client_credentials"
  client_id: "{secrets.client_id}"
  client_secret: "{secrets.client_secret}"
  authentication_url: "https://api.example.com/oauth/token"
  scopes:
    - "read:data"
```

**Authorization Code Flow (Browser-Based):**

For user-interactive authentication. Sling opens a browser for authorization and handles the callback automatically.

```yaml
authentication:
  type: "oauth2"
  flow: "authorization_code"
  client_id: "{secrets.client_id}"
  client_secret: "{secrets.client_secret}"
  authentication_url: "https://api.example.com/oauth/token"
  authorization_url: "https://api.example.com/oauth/authorize"
  scopes:
    - "read:data"
```

**Device Code Flow (Headless/CLI):**

For environments without a browser. Sling displays a URL and code for the user to enter on another device.

```yaml
authentication:
  type: "oauth2"
  flow: "device_code"
  client_id: "{secrets.client_id}"
  authentication_url: "https://api.example.com/oauth/token"
  device_auth_url: "https://api.example.com/oauth/device/code"
  scopes:
    - "read:data"
```

### AWS Signature V4 Authentication

For AWS services that require Signature V4 authentication (e.g., S3, AppSync, API Gateway):

```yaml
authentication:
  type: "aws-sigv4"
  aws_service: "execute-api"  # The AWS service name (e.g., s3, execute-api, appsync)
  aws_region: "{env.AWS_REGION}"
  aws_access_key_id: "{secrets.aws_access_key_id}"
  aws_secret_access_key: "{secrets.aws_secret_access_key}"
  aws_session_token: "{secrets.aws_session_token}"  # Optional, for temporary credentials
  aws_profile: "{env.AWS_PROFILE}"  # Optional, use AWS profile instead of explicit keys
```

> 📝 **Note:** For AWS authentication, you can use either explicit credentials (`aws_access_key_id`, `aws_secret_access_key`) or AWS profiles (`aws_profile`). The AWS SDK credential chain is also supported.

### HMAC Authentication

For APIs that require HMAC (Hash-based Message Authentication Code) request signing, such as cryptocurrency exchanges (Kraken, Binance) or custom enterprise APIs:

```yaml
authentication:
  type: "hmac"
  algorithm: "sha256"  # sha256 or sha512
  secret: "{secrets.api_secret}"
  secret_encoding: "hex"  # Optional: "hex", "base64", or "raw" (default)
  signing_string: "{http_method}{http_path}{unix_time}{http_body_sha256}"
  request_headers:
    X-Signature: "{signature}"
    X-Timestamp: "{unix_time}"
    X-API-Key: "{secrets.api_key}"
  nonce_length: 16  # Optional: generates random nonce (in bytes)
```

**How HMAC Works:**

1. A signing string is constructed from request components (method, path, timestamp, body hash, etc.)
2. The string is signed using HMAC-SHA256 or HMAC-SHA512 with your secret key
3. The signature and related headers are automatically added to each request

**Secret Encoding:**

Some APIs provide secrets in encoded formats (hex or base64) that must be decoded to raw bytes before signing. Use `secret_encoding` (*v1.5.6+*) to specify how to decode the secret:

* `"raw"` or `""` (default) - Use secret as-is (plain string)
* `"hex"` - Decode secret from hexadecimal string to bytes
* `"base64"` - Decode secret from base64 string to bytes

**Available Variables for Signing:**

* `http_method` - HTTP method (GET, POST, etc.)
* `http_path` - Request path with query parameters
* `http_query` - Canonical query string (sorted alphabetically by key)
* `http_headers` - Canonical headers (lowercase, sorted, newline-separated)
* `http_body_raw` - Raw request body as string
* `http_body_md5` - MD5 hash of request body (hex-encoded)
* `http_body_sha1` - SHA1 hash of request body (hex-encoded)
* `http_body_sha256` - SHA256 hash of request body (hex-encoded)
* `http_body_sha512` - SHA512 hash of request body (hex-encoded)
* `unix_time` - Unix timestamp in seconds
* `unix_time_ms` - Unix timestamp in milliseconds
* `date_iso` - ISO 8601 formatted date (e.g., "2023-10-31T15:30:32Z")
* `date_rfc1123` - RFC 1123 formatted date (e.g., "Tue, 31 Oct 2023 15:30:32 GMT")
* `nonce` - Random hex string (if `nonce_length` is set)
* `signature` - Computed HMAC signature (hex-encoded, only available in `request_headers`)

**Example: Kraken-style Authentication**

```yaml
authentication:
  type: "hmac"
  algorithm: "sha256"
  secret: "{secrets.api_secret}"
  signing_string: "{http_method}{http_path}{nonce}{http_body_sha256}"
  request_headers:
    API-Key: "{secrets.api_key}"
    API-Sign: "{signature}"
    API-Nonce: "{nonce}"
  nonce_length: 16
```

**Example: Hex-encoded Secret**

Some APIs provide secrets as hex strings that must be decoded before signing:

```yaml
authentication:
  type: "hmac"
  algorithm: "sha256"
  secret: "{secrets.api_secret}"
  secret_encoding: "hex"
  signing_string: "{http_body_raw}"
  request_headers:
    Content-Type: "application/json"
    X-API-Key: "{secrets.api_key}"
    X-API-Signature: "{signature}"
```

> 📝 **Note:** The `signing_string` and `request_headers` templates support all Sling template variables including `{secrets.*}`, `{env.*}`, and `{state.*}`.

### Sequence Authentication (Custom Workflows)

For APIs requiring a custom authentication sequence (e.g., multi-step login), use the `sequence` type. This allows defining a series of API calls to obtain authentication tokens or session data.

```yaml
authentication:
  type: "sequence"
  expires: 3600   # re-auth every 1 hr
  sequence:
    - request:
        url: "/login"
        method: POST
        payload:
          site_id: "{secrets.site_id}"
          user_id: "{secrets.user_id}"
          password: "{secrets.password}"
      response:
        processors:
          - expression: "response.json.token"
            output: "state.token"   # you can use this in you main request block
            aggregation: last
```

This performs a login request and extracts the token into `state.token` for use in subsequent requests. See [Requests & Responses](https://github.com/slingdata-io/sling-docs/blob/master/concepts/api/requests.md) for more on sequences.

## Endpoint-Level Authentication

By default, all endpoints use the authentication configured at the spec level. However, individual endpoints can override or disable authentication.

### Overriding Authentication

An endpoint can specify its own authentication that overrides the spec-level configuration:

```yaml
authentication:
  type: "basic"
  username: "{secrets.username}"
  password: "{secrets.password}"

endpoints:
  # Uses spec-level basic auth
  users:
    request:
      url: "{state.base_url}/users"

  # Uses its own OAuth2 auth instead
  admin_data:
    authentication:
      type: "oauth2"
      flow: "client_credentials"
      client_id: "{secrets.admin_client_id}"
      client_secret: "{secrets.admin_client_secret}"
      authentication_url: "https://api.example.com/oauth/token"
    request:
      url: "{state.base_url}/admin/data"
```

### Disabling Authentication

Some endpoints (like health checks or public data) may not require authentication. Set `authentication: null` to disable it:

```yaml
authentication:
  type: "oauth2"
  # ... OAuth config for most endpoints

endpoints:
  # No authentication needed for health check
  health:
    authentication: null
    request:
      url: "{state.base_url}/health"

  # Uses spec-level OAuth2
  protected_data:
    request:
      url: "{state.base_url}/data"
```

## Authentication Expiry

The `expires` property can be used with any authentication type to force re-authentication after a specified number of seconds. This is useful when tokens or sessions have a fixed lifetime.

```yaml
authentication:
  type: "basic"
  username: "{secrets.username}"
  password: "{secrets.password}"
  expires: 3600  # Re-authenticate every hour
```

When authentication expires, Sling automatically re-authenticates before the next request. This happens transparently without interrupting data extraction.

## Authentication Method Comparison

| Method      | Best For                          | Auto-Refresh       | Interactive     |
| ----------- | --------------------------------- | ------------------ | --------------- |
| `static`    | API keys, bearer tokens           | N/A                | No              |
| `basic`     | Username/password APIs            | N/A                | No              |
| `oauth2`    | Modern APIs, delegated auth       | Yes                | Depends on flow |
| `aws-sigv4` | AWS services                      | Yes                | No              |
| `hmac`      | Crypto exchanges, signed requests | N/A                | No              |
| `sequence`  | Custom auth workflows             | No (use `expires`) | No              |
