# Authentication

> JWT-based authentication for the ZopNight API. Login, signup, SAML SSO, token refresh, PATs, and request headers.

Source: https://zop.dev/developer-docs/overview/authentication

---

ZopNight uses JWT-based authentication. All API requests (except auth endpoints) require a valid access token.

## Overview

The authentication flow works as follows:

1. Authenticate via login or SSO to receive an access token and refresh token
2. Include the access token in the `Authorization` header for all API requests
3. When the access token expires, use the refresh token to obtain a new one
4. The gateway validates the JWT, extracts your organization context, and routes the request

**Info**

Access tokens are RS256-signed JWTs containing your user identity and organization (tenant) context. The gateway validates tokens using JWKS (JSON Web Key Set) from the auth service.

## Authentication Providers

| Provider | Method | Description |
|---|---|---|
| **Email/Password** | `email` | Standard email and password with OTP verification |
| **Google** | `google` | OAuth 2.0 with Google accounts |
| **GitHub** | `github` | OAuth 2.0 with GitHub accounts |
| **SAML** | `saml` | Enterprise SSO via SAML 2.0 identity providers |

## Login

### POST /auth/login

Authenticate and receive tokens.

```bash title="Email/password login"
curl -X POST https://zopnight.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@company.com",
    "password": "your-password",
    "provider": "email"
  }'
```

```bash title="Google OAuth login"
curl -X POST https://zopnight.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "google",
    "token": "<google-oauth-id-token>"
  }'
```

```json title="Response"
{
  "data": {
    "accessToken": "eyJhbGciOiJSUzI1NiIs...",
    "refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
    "tenants": [
      {
        "id": "org_abc123",
        "name": "Acme Corp"
      }
    ],
    "user": {
      "email": "user@company.com",
      "name": "Jane Doe"
    }
  }
}
```

## Signup

### POST /auth/signup

Create a new user account.

```bash title="Request"
curl -X POST https://zopnight.com/api/auth/signup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newuser@company.com",
    "password": "secure-password",
    "name": "Jane Doe"
  }'
```

After signup, verify your email using the OTP sent to your inbox.

## Email Verification

### POST /auth/email/verify

Request a verification OTP.

```bash title="Request"
curl -X POST https://zopnight.com/api/auth/email/verify \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@company.com" }'
```

### PATCH /auth/email/otp/validate

Validate the OTP code.

```bash title="Request"
curl -X PATCH https://zopnight.com/api/auth/email/otp/validate \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@company.com",
    "otp": "123456"
  }'
```

## Token Refresh

Access tokens are short-lived. Use the refresh token to obtain a new access token before expiry.

### POST /auth/refresh

Exchange a refresh token for a new access token.

```bash title="Request"
curl -X POST https://zopnight.com/api/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refreshToken": "dGhpcyBpcyBhIHJlZnJl...",
    "tenantID": "org_abc123"
  }'
```

```json title="Response"
{
  "data": {
    "accessToken": "eyJhbGciOiJSUzI1NiIs...(new token)",
    "refreshToken": "bmV3IHJlZnJlc2ggdG9r..."
  }
}
```

**Tip**

Refresh the token before it expires (e.g., when less than 60 seconds remain). This avoids interrupted API calls due to expired tokens.

## SAML SSO

Enterprise organizations can configure SAML 2.0 single sign-on with their identity provider.

### POST /auth/saml/initiate

Begin a SAML authentication flow.

```bash title="Request"
curl -X POST https://zopnight.com/api/auth/saml/initiate \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@enterprise.com" }'
```

Returns a redirect URL to your organization's identity provider. After authentication, the IdP redirects back with a SAML assertion that is exchanged for JWT tokens.

## Create Organization

### POST /auth/v2/organisation

Create a new organization for your account.

```bash title="Request"
curl -X POST https://zopnight.com/api/auth/v2/organisation \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "My Company" }'
```

## Password Reset

### POST /auth/password/forget

Initiate a password reset.

```bash title="Request"
curl -X POST https://zopnight.com/api/auth/password/forget \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@company.com" }'
```

### POST /auth/password/reset

Complete the password reset.

```bash title="Request"
curl -X POST https://zopnight.com/api/auth/password/reset \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@company.com",
    "otp": "123456",
    "newPassword": "new-secure-password"
  }'
```

## Logout

### POST /auth/logout

Invalidate the current session.

```bash title="Request"
curl -X POST https://zopnight.com/api/auth/logout \
  -H "Authorization: Bearer <token>"
```

## Personal Access Tokens (PAT)

For CI pipelines, scripts, and other non-interactive clients, ZopNight supports Personal Access Tokens. PATs use the same `Authorization: Bearer` header as JWT access tokens but are validated against the auth service directly rather than through JWKS — they don't expire on the same schedule as user JWTs.

| Property | Value |
|---|---|
| **Prefix** | `zn_pat_` followed by an opaque secret |
| **Where to use** | `Authorization: Bearer zn_pat_...` |
| **Created from** | The ZopNight UI under Settings → Personal Access Tokens |
| **Scope** | Inherits the creating user's role and permissions |

```bash title="Using a PAT"
curl https://zopnight.com/api/resources \
  -H "Authorization: Bearer zn_pat_xxxxxxxxxxxxxxxxxxxxxxxx"
```

**Warning**

A PAT grants the same access as the user who created it. Store them in your secret manager, never in source control, and rotate periodically.

## Organization Context

Most JWTs include the active organization in the `tenant-id` or `org_id` claim, and that value is used to scope every request. If a token does not carry an org claim (for example, when a user belongs to multiple orgs and the client wants to override the default), pass an explicit header:

| Header | Value | When to use |
|---|---|---|
| `X-Org-Id` | The target organization's ID | Override the JWT's default org, or supply org context for tokens that lack one. |

## Unauthenticated Endpoints

A small number of endpoints intentionally bypass JWT validation. They use other mechanisms (HMAC signatures, body-carried JWTs, or no auth at all):

| Endpoint | Authentication | Purpose |
|---|---|---|
| `POST /webhooks/github` | HMAC-SHA256 signature in `X-Hub-Signature-256` | GitHub push & PR webhooks for the deploy pipeline |
| `POST /build-callbacks/{revisionID}` | JWT carried in the request body, signed with the build callback secret | Build runner reports back when an image is ready |
| `GET /.well-known/openid-configuration` | Public | OIDC discovery (used by Azure Workload Identity Federation) |
| `GET /.well-known/jwks.json` | Public | JSON Web Key Set for verifying ZopNight-issued tokens |
| `POST /auth/*` | Varies (login, signup, refresh) | Authentication flows themselves |

## Request Headers

| Header | Value | Required |
|---|---|---|
| `Authorization` | `Bearer <access_token \| PAT>` | Yes (all endpoints except those listed above) |
| `X-Org-Id` | An organization ID | Optional — overrides the org claim in the JWT |
| `Content-Type` | `application/json` | Yes (for POST/PUT/PATCH requests) |
