---
title: "Protected environments"
description: "Require sign-in for every public endpoint in an environment, enforced at the edge, with a signed header that tells your service who is signed in."
---

A protected environment requires sign-in before any request reaches your services. Specific enforces this at the edge, so it applies to every public endpoint and every domain in the environment without any application code.

Turn it on per environment from the [dashboard](https://dashboard.specific.dev), under the environment's settings: **Require sign-in for services**. Toggling it redeploys the environment so it takes effect immediately.

Protected environments are included in the Scale plan and above; see the [pricing page](https://specific.dev/pricing).

## Who can sign in

Members of the Specific organization that owns the project. Signing in goes through the same identity provider you use for Specific itself, and anyone who isn't a member of the owning organization is refused.

That makes protected environments a fit for internal tools, staging environments, and admin panels. It is **not** a login for your own end users, who don't have Specific accounts. Build that authentication in your application.

## Identifying the signed-in user

Once an environment is protected, every request reaches your service with an `X-Specific-Token` header: an HS256-signed JWT describing who is signed in. Reference the endpoint's `token_secret` in your `specific.hcl` to receive the verification key:

```hcl specific.hcl
service "web" {
  build = build.web

  endpoint {
    public = true
  }

  env = {
    SPECIFIC_TOKEN_SECRET = endpoint.default.token_secret
  }
}
```

Only services that ask for the key receive it, and it is scoped to a single endpoint of a single environment: a token minted for staging can't verify in production. See [Endpoints](/guides/services#multiple-named-endpoints) for the other endpoint reference attributes.

The token's claims:

| Claim   | Meaning                                                               |
| ------- | --------------------------------------------------------------------- |
| `sub`   | Stable Specific user ID (`user_…`). Use this to key your own records. |
| `email` | The signed-in user's verified email. Absent if we couldn't verify one. |
| `host`  | The hostname the token was issued for.                                |
| `env`   | The environment ID (`env_…`).                                         |
| `iat`   | Issue time.                                                           |
| `exp`   | Expiry, 24 hours after sign-in.                                       |

A few things that differ from a typical auth setup:

- The header is not stripped on unprotected endpoints, so a request carrying one doesn't mean protection is on. Only a valid signature makes it trustworthy; treat a missing or unverifiable token as unauthenticated.
- The `__specific_sess_v2` cookie is the session credential, not an identity assertion. Don't parse it; the header is the supported interface.
- Any service in the same environment can send this header to another. The trust boundary is the environment, not the individual service.

### Revocation

The signed-in session lasts 24 hours. Removing someone from the organization stops them signing in again, but an existing session keeps working until it expires.

## Local development

`specific dev` runs your services directly on localhost with no edge in front, so **no `X-Specific-Token` header is sent locally**. `endpoint.<name>.token_secret` still resolves, to a fixed development value, so your service starts normally.

Treat the missing header as unauthenticated and add a dev-only fallback user that can't be reached in production. Because the local key is a known constant, you can also sign your own tokens with it to exercise your authorization logic against `specific dev`.
