# Authentication

The Go SDK supports manual web-based login (request token → access token) and fully automated login with TOTP.

## Prerequisites

Before authenticating, ensure you have:

- Valid Arrow user credentials
- Registered redirect URL in the [Developer Apps](https://app.arrow.trade) section
- Your `appID` and `appSecret` from the Trading API section
- Static IP registered (mandatory per [SEBI Circular](https://www.sebi.gov.in/legal/circulars/feb-2025/safer-participation-of-retail-investors-in-algorithmic-trading_91614.html))

## Authentication Methods

### Method 1: Web-based Login (Manual)

Redirect users to Arrow's login page, then exchange the callback `request-token`.

```go
package main

import (
    "fmt"
    "log"

    "github.com/arrow-trade/go-arrow/arrow"
)

func main() {
    client := arrow.NewClient("YOUR_APP_ID", "YOUR_APP_SECRET")

    // Prints https://app.arrow.trade/app/login?appId=<appID> and reads the token from stdin
    client.Login()

    fmt.Println("Access token:", client.GetToken())
}
```

If you already have the request token (for example from your OAuth callback), skip the stdin prompt:

```go
token, err := client.Authenticate("request_token_from_callback")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Access token:", token)
```

`Authenticate` SHA256-hashes `appId:appSecret:request-token` and POSTs to `/auth/app/authenticate-token` on `https://edge.arrow.trade`. It sends both `checkSum` and `checksum` in the JSON body.

!!! info "Login URL query key"
    `Login()` prints `?appId=` (lowercase `d`). Build the same URL yourself with `fmt.Sprintf("https://app.arrow.trade/app/login?appId=%s", appID)`.

!!! info "Callback URL"
    After successful login, Arrow redirects to your registered URL with:

    - `request-token`: Temporary authentication token
    - `checksum`: SHA256 hash for verification

### Method 2: Automated Login (TOTP)

`AutoLogin` runs the full credential + TOTP + token-exchange flow. The app secret is taken from `NewClient`, not passed again.

| Parameter | Required | Description |
|-----------|----------|-------------|
| `username` | ✓ | Arrow user ID |
| `password` | ✓ | Account password |
| `totpSecret` | ✓ | Base32 TOTP secret |

```go
if err := client.AutoLogin("YOUR_USER_ID", "YOUR_PASSWORD", "YOUR_TOTP_SECRET"); err != nil {
    log.Fatal(err)
}
fmt.Println("Logged in. Token:", client.GetToken())
```

**Flow:**

1. POST `https://api.arrow.trade/auth/app/login`
2. Generate a 6-digit TOTP (30s period, SHA1)
3. POST `https://edge.arrow.trade/auth/validate-2fa`
4. Parse `request-token` from the redirect URL
5. `Authenticate(requestToken)` — stores `Token` on the client

!!! tip "TOTP Secret"
    The `totpSecret` is the base32 encoded secret used to generate time-based one-time passwords. You can go to the [profile page](https://app.arrow.trade/profile) and copy the TOTP secret if enabled. If not enabled, log out of the current session and click on forgot password and complete the flow.

## Session Management

### Get Current Token

```go
token := client.GetToken()
```

### Set Token Manually

Reuse a valid token from a previous session (still within 24 hours):

```go
client.SetToken("your_existing_access_token")
```

### Clear Session

There is no `InvalidateSession` helper. Clear the token:

```go
client.SetToken("")
```

## Token Lifecycle

| Aspect | Details |
|--------|---------|
| **Validity** | 24 hours from generation |
| **After expiry** | New login required — refresh tokens are not supported |
| **Storage** | Store securely; never expose in client-side code |

!!! warning "Token Expiration"
    Access tokens expire after 24 hours due to regulatory compliance. Implement a re-login path in long-running processes.

## Authentication Response

`Authenticate` returns the access token string and updates `client.Config`. The wire payload looks like:

```json
{
  "status": "success",
  "data": {
    "name": "ABHISHEK JAIN",
    "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
    "userId": "AJ0001"
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | User's full name |
| `token` | string | JWT access token |
| `userId` | string | Unique user identifier |

## User Information

After authentication, retrieve profile and limits:

```go
user, err := client.GetUserDetails()
if err != nil {
    log.Fatal(err)
}
fmt.Println("Logged in as:", user.Data.Name)
fmt.Println("Exchanges:", user.Data.Exchanges)

limits, err := client.GetLimits()
if err != nil {
    log.Fatal(err)
}
fmt.Println("Usable margin:", limits.Data.Margin["usableMargin"])
```

!!! note "Wrapped user and limits"
    `GetUserDetails` returns `*User` with `Data` + `Status` (use `user.Data.Name`). `GetLimits` is the same (`limits.Data.Margin`). Positions, holdings, and order book return **unwrapped slices**.

## Error Handling

```go
if err := client.AutoLogin(userID, password, totpSecret); err != nil {
    log.Printf("Authentication failed: %v", err)
    return
}
```

### Common Errors

| Error | Cause | Solution |
|-------|-------|----------|
| Invalid checksum | Incorrect SHA256 generation | Verify `appId:appSecret:request-token` format |
| Token expired | Request token timeout | Restart authentication flow |
| Invalid credentials | Wrong user ID or password | Verify credentials |
| Invalid TOTP | Incorrect or expired OTP | Check TOTP secret and system time sync |

## Security Best Practices

!!! danger "Security Notice"
    - **Never** expose `appSecret` in client-side code
    - **Never** commit credentials to version control
    - **Always** use environment variables for sensitive data
    - **Always** use HTTPS for all API communications

### Environment Variables Example

```go
package main

import (
    "log"
    "os"

    "github.com/arrow-trade/go-arrow/arrow"
)

func main() {
    client := arrow.NewClient(os.Getenv("ARROW_APP_ID"), os.Getenv("ARROW_APP_SECRET"))

    if err := client.AutoLogin(
        os.Getenv("ARROW_USER_ID"),
        os.Getenv("ARROW_PASSWORD"),
        os.Getenv("ARROW_TOTP_SECRET"),
    ); err != nil {
        log.Fatal(err)
    }
}
```

## Complete Example

```go
package main

import (
    "fmt"
    "log"
    "os"

    "github.com/arrow-trade/go-arrow/arrow"
)

func initializeClient() (*arrow.Client, error) {
    client := arrow.NewClient(os.Getenv("ARROW_APP_ID"), os.Getenv("ARROW_APP_SECRET"))

    if err := client.AutoLogin(
        os.Getenv("ARROW_USER_ID"),
        os.Getenv("ARROW_PASSWORD"),
        os.Getenv("ARROW_TOTP_SECRET"),
    ); err != nil {
        return nil, fmt.Errorf("login: %w", err)
    }

    user, err := client.GetUserDetails()
    if err != nil {
        return nil, fmt.Errorf("user details: %w", err)
    }
    fmt.Println("Logged in as:", user.Data.Name)
    return client, nil
}

func main() {
    client, err := initializeClient()
    if err != nil {
        log.Fatal(err)
    }
    _ = client
}
```
