Skip to content

Authentication

Aspected supports two authentication mechanisms: static API tokens and granular access tokens. Both can be used independently or together to secure your instance.


Static API Token ≥0.1.0

A static API token is the simplest way to authenticate requests. When configured, every request must include the token in one of the following headers:

Api-Key: <your-token>
Authorization: Bearer <your-token>

Note

If granular access tokens are also enabled, the Authorization: Bearer header is reserved for JWT tokens. In that case, the static API token can only be supplied via the Api-Key header.

To enable static token authentication, set the server.staticApiToken configuration option:

config.yml
server:
  staticApiToken: "my-secret-token"

Or via an environment variable:

export ASPECTED_SERVER_STATIC_API_TOKEN="my-secret-token"

Enable HTTPS when using an API token

If you have set a staticApiToken and are exposing Aspected directly to the internet, you must enable HTTPS. Without TLS, the API token is sent in plain text over the network and can be easily intercepted.


Granular Access Tokens ≥0.3.0

Licensed only

This feature requires an enterprise license.

Granular access tokens provide the ability to create multiple bearer tokens that each have their own set of granular scoped access controls, rather than a single shared secret that grants full access. Granular Access tokens are built on the JSON Web Tokens (JWT) standard. JWTs are passed via the Authorization: Bearer header:

Authorization: Bearer <jwt-token>

JWTs work by signing the contents of the token itself using a shared secret or asymmetric keys. Aspected supports both through two signing algorithms, but only one can be active at a time:

  • HS256 — symmetric key (shared secret)
  • RS256 — asymmetric key (RSA public key verification)

Configure whichever one you intend to use:

config.yml
server:
  jwt:
    secret: "a-32-byte-or-longer-hs256-secret-value"
config.yml
server:
  jwt:
    publicKeyPath: /path/to/public.pem

Or via an environment variable:

export ASPECTED_SERVER_JWT_SECRET="a-32-byte-or-longer-hs256-secret-value"
export ASPECTED_SERVER_JWT_PUBLIC_KEY_PATH=/path/to/public.pem

HS256 secret length

The HS256 secret must be at least 256 bits (32 bytes) long. Shorter secrets are rejected and the server will fail to start.

Only one algorithm is used

If both secret and publicKeyPath are set, RS256 takes priority and the HS256 secret is ignored. To use HS256, make sure no public key path is configured.

Generating JSON Web Tokens

Aspected itself cannot generate tokens for you. Instead, tokens can be generated client-side and offline through a third-party library or service and then signed using the configured secret or private key, for example:

A standard JWT consists of a header, payload, and signature. The header contains the signature type, which must match the configured signature type:

{
  "alg": "HS256",
  "typ": "JWT"
}

The payload can contain the information of the token itself, such as the issuer, expiration date, access levels, etc.:

{
  "sub": "reader-service",
  "iat": 1723456789,
  "exp": 1723460389,
  "acc": "read"
}

There is a set of default supported properties, called registered claims, as defined by the standard (RFC 7519, §4.1). All of these are optional, and only the fields exp (expiration), iat (issued at), and nbf (not before) are used by the server to determine the validity of the token. The other claims can still be worthwhile to include when audit logging is enabled, as the decoded JWT is included in the recorded events, so these claims can still provide a useful tool for extra traceability.

Set an issued-at and expiration time

It is strongly recommended to set both iat and exp on every token. Aspected has no mechanism to revoke an individual JWT once it has been issued — the only way to invalidate a token is to rotate the signing keys, which invalidates all issued tokens at once. Giving tokens a reasonable lifetime ensures that leaked or stale credentials expire on their own.

Beyond the standard registered claims, tokens carry a set of Aspected-specific access rules that determine what the bearer is permitted to do. These are described below in the Access Rules section.

Access Rules

The JWT payload contains access control rules that determine what the bearer is allowed to do. Permissions use four access levels, ordered from least to most privileged:

  • deny — No access.
  • read — Read-only operations (search, get, list).
  • write — Read access plus mutation operations (upload documents).
  • manage — Full access including creation and deletion.

A given access level always includes everything granted by the levels below it.

Global permission (acc)

The acc field sets the global default access level. If omitted, it defaults to deny.

{
  "acc": "read"
}

This grants read access to all scopes unless overridden.

Scope overrides

Each scope can override the global permission by specifying a different access level as a simple string:

{
  "acc": "deny",
  "indexes": "manage"
}

This denies access globally but grants full management access to all index operations.

Resource-level rules (patterns)

For finer control, a scope can define an array of resource-level rules instead of a single access string. Each rule matches resources by a glob pattern:

{
  "acc": "deny",
  "indexes": [
    {
      "pat": "public-*",
      "acc": "read"
    },
    {
      "pat": "internal-*",
      "acc": "manage"
    }
  ]
}

Patterns are matched specificity. For example, the value test123 will match all of these rules:

  • test123 — Exact match
  • test123* — Matches exactly, but the wildcard character makes it slightly less specific
  • test??? — Matches the three placeholder characters exactly
  • test* — Wildcard matches are less exact than placeholder matches
  • * — Matches everything, least specific

This list is ordered from most specific to least specific, so the value test123 will match the access level for the pattern test123 in this case. If that pattern didn't exist, it would have matched test123* and so on.

If one rule must match before any other, a priority can be set through the pri field, where higher numbers take precedence over lower numbers. The default priority is zero.

Field Default Description
pat Glob pattern matched against the resource name (e.g. the index name).
acc deny Access level granted when the pattern matches.
pri 0 Priority — higher values take precedence when multiple patterns match.

Examples

Read-only access to all indexes
{
  "sub": "reader-service",
  "iat": 1723456789,
  "acc": "read"
}
Write access only to indexes matching project-*
{
  "sub": "ingestion-service",
  "iat": 1723456789,
  "acc": "deny",
  "indexes": [
    {
      "pat": "project-*",
      "acc": "write"
    }
  ]
}
Full management for a specific index, read for all other indexes
{
  "sub": "admin",
  "iat": 1723456789,
  "acc": "deny",
  "indexes": [
    {
      "pat": "*",
      "acc": "read"
    },
    {
      "pat": "my-index",
      "acc": "manage"
    }
  ]
}

Actions and Required Permissions

Every operation maps to an action, and each action requires a minimum access level to be permitted. Actions are either scoped to a resource type or global.

Action Scope Resource Required permission
get_status read
list_indexes indexes read
get_index indexes Index name read
search_index indexes Index name read
list_docs indexes Index name read
get_doc indexes Index name read
upload_docs indexes Index name write
create_index indexes Index name manage
delete_index indexes Index name manage

Info

While the list_indexes action is not tied to a specific resource for access, the response only includes the indexes the bearer has access to. If the token does not allow access to any index, the endpoint is not accessible.