Skip to content
English
  • There are no suggestions because the search field is empty.

Eyemagnet Auth API - Integration Guide

To integrate with Eyemagnet Auth, send GraphQL requests over HTTPS using the base URL and credentials supplied by Eyemagnet.

This guide is for partners integrating an application or backend with Eyemagnet Auth. The API is GraphQL over HTTPS. It issues signed JWTs that can be used both with this API and with services that trust Eyemagnet Auth.

The API is a GraphQL authentication service, not an OAuth or OpenID Connect authorization-code endpoint. Keep client secrets in a trusted backend; do not embed them in a browser, mobile app, desktop app, or published source code.


Before you start

Eyemagnet will provide an API base URL plus credentials for the integration:

Value Purpose Keep secret?
API base URL Base path for GraphQL and the supporting HTTP endpoints No
Client ID and client secret Identifies the application requesting tokens Yes — backend only
Key ID and key secret, if applicable Identifies a machine or service principal Yes — backend only
Allowed scopes and teams Defines the actions and tenant context the integration may use No

In the examples below, replace https://auth.example.com/auth/v2 with the API base URL supplied for your environment. The GraphQL endpoint is the base URL itself (a trailing slash is also accepted).

Endpoint Method Purpose
{base} POST GraphQL operations
{base}/.well-known/jwks.json GET Public signing keys for JWT validation
{base}/_health GET Health check; returns HEALTHY

The configured base path is environment-specific, so construct the JWKS and health URLs from the supplied base URL rather than hard-coding a path.


Calling GraphQL

Send JSON with a GraphQL query and optional variables object. Protected operations use an access token in the Authorization header.

POST /auth/v2 HTTP/1.1
Host: auth.example.com
Content-Type: application/json
Authorization: Bearer <access-token>

{
  "query": "query CurrentAccount { me { id name email } }"
}

GraphQL can return a successful HTTP response with an errors array. Treat a response as successful only when the relevant item is present under data and there are no applicable errors. The current API response formatter returns an error code and name alongside message; tolerant GraphQL clients may also check extensions.code when they normalize errors:

code Meaning Typical action
AUTHENTICATION Missing or invalid authentication Obtain a new token and retry once.
AUTHORIZATION Client, credential, or team access is not allowed Do not retry; check the integration configuration.
SCOPE Token does not hold the required scope Request or issue a token with the necessary scope.
MEMBERSHIP Subject cannot act in the selected team Select a permitted team.
CREDENTIALS Username or password was rejected Prompt the user again; do not reveal which field failed.
TFA A second factor is required Ask for the current TOTP or recovery code, then retry login.
EXPIRED Bearer or refresh token has expired Refresh or authenticate again.
ACCOUNT_LOCKED Too many failed logins Back off and let the user try later.
PASSWORD_POLICY Proposed password does not meet policy Display the returned policy message.

The service throttles sensitive operations. Honor 429 responses and use bounded exponential backoff. Never retry invalid credentials in a loop.


Obtain an access token

An authentication response contains a short-lived bearer token and a longer lived refresh token. Always ask for JWT version v2 when minting new tokens.

Password authentication

Use this from a confidential backend after the user supplies their credentials. account.username accepts the account username or email address. Supply team when the user needs a particular team context; otherwise the service chooses an accessible default.

const response = await fetch("https://auth.example.com/auth/v2", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    query: `
      mutation PasswordLogin(
        $client: ClientLogin!
        $account: AccountLogin!
        $team: Reference
      ) {
        passwordAuthentication(client: $client, account: $account, team: $team) {
          bearerToken(version: "v2")
          refreshToken(version: "v2")
        }
      }
    `,
    variables: {
      client: { id: "<client-id>", secret: "<client-secret>" },
      account: { username: "person@example.com", password: "<password>" },
      team: { id: "<team-id>" },
    },
  }),
});

const { data, errors } = await response.json();
if ( !response.ok || errors?.length ) throw new Error( errors?.[ 0 ]?.message || "Authentication failed" );

const { bearerToken, refreshToken } = data.passwordAuthentication;

Example response:

{
  "data": {
    "passwordAuthentication": {
      "bearerToken": "eyJ...",
      "refreshToken": "eyJ..."
    }
  }
}

By default, a bearer token is valid for up to 6 hours and a refresh token for up to 12 hours. The optional ttl parameter is in seconds and can shorten a token lifetime. Store refresh tokens only in encrypted server-side storage or a secure, HTTP-only session mechanism.

Machine-key authentication

For service-to-service work, use a provisioned key rather than a human password. A request needs both the client credentials and the key credentials. The key’s roles and explicit scopes determine what the resulting token may do.

mutation KeyLogin($client: ClientLogin!, $key: KeyLogin!) {
  keyAuthentication(client: $client, key: $key) {
    bearerToken(version: "v2")
    refreshToken(version: "v2")
  }
}
{
  "client": { "id": "<client-id>", "secret": "<client-secret>" },
  "key": { "id": "<key-id>", "secret": "<key-secret>" }
}

Use a dedicated key per deployment or workload. Rotate it by provisioning a replacement, deploying the new secret, and then retiring the old key. Do not share a key between unrelated services.

Other authentication methods

The following mutations are available when enabled for the client and account:

Mutation Use
providerAuthentication Exchange a verified Apple, Google, Microsoft, or Canva provider token for Eyemagnet tokens.
webAuthenticationOptions then webAuthentication Authenticate with a registered WebAuthn passkey.
switchAuthentication Mint a token whose default team is another accessible team.
refreshAuthentication Replace an expired or expiring bearer token using its matching refresh token.

Provider authentication takes a provider token obtained using that provider’s SDK or flow. The application must verify that its integration is configured for the provider before relying on this flow.


Use and refresh tokens

Use the bearer token with every protected request:

Authorization: Bearer <bearer-token> 

To refresh, send the current bearer token in the Authorization header and its matching refresh token as an argument. The bearer token may be expired, but it must be the token that the refresh token was minted alongside, and the same client must perform the refresh.

mutation Refresh($client: ClientLogin!, $refreshToken: String!) {
  refreshAuthentication(client: $client, refreshToken: $refreshToken) {
    bearerToken(version: "v2")
    refreshToken(version: "v2")
  }
}
{
  "client": { "id": "<client-id>", "secret": "<client-secret>" },
  "refreshToken": "<refresh-token>"
}

Replace both tokens atomically after every successful refresh. If a refresh fails, discard the pair and begin a new authentication flow. A password, passkey, or other sign-in-method change prevents an older bearer token from being refreshed. If immediate, account-level revocation is important to your service, also call attest before privileged work rather than relying on local JWT verification alone.

To change the active tenant while signed in, call switchAuthentication(client: ..., team: { id: ... }) with the current bearer token. The requested team must already be in the token subject’s accessible team tree.


Verify tokens in your service

Services that accept Eyemagnet tokens must verify the signature and standard JWT validity claims before trusting any custom claim.

  1. Fetch {base}/.well-known/jwks.json and cache it according to its response headers.
  2. Select the key matching the JWT header’s kid.
  3. Verify the token with RS256; reject any other algorithm.
  4. Validate exp and, when present, nbf.
  5. Enforce the client (iss), team (aud), and scope (sco) appropriate to the action. Do not authorize solely because a token has a valid signature.

The API also provides attest(token: String!): Boolean! for a remote validity check. It verifies the signature, validity window, and account token-revocation stamp. Local JWKS verification is usually preferable on the request path; use attest when a live revocation check is required.

JWT claims

Claim Meaning
jti Unique token ID.
iss Eyemagnet client ID that issued the token.
sub Account ID or machine-key ID represented by the token.
aud Array of accessible team IDs. The first ID is the current/default team.
sco Granted scope names. A scope can be resource-limited as scope name::resourceId.
iat, exp, nbf Issued-at, expiry, and optional not-before timestamps (Unix seconds).
pln Optional granted feature names.
meta Optional caller-requested display metadata. Do not use it for authorization.

Common API operations

These examples use the bearer token header described above. Each resolver also enforces its own scope and team visibility rules.

Inspect the current identity

query CurrentContext {
  app { id name }
  me {
    id
    name
    email
    verified
    memberships { team { id name } role { id name } }
  }
  us { id name }
}

app, me, and us require query clients, query accounts, and query us, respectively.

Page through resources

List operations return an edge with results, cursor, and total. Pass the returned cursor to retrieve the next page; a null cursor means there are no more results.

query Accounts($cursor: ID) {
  accounts(cursor: $cursor, limit: 50, sort: { name: ASCENDING }) {
    cursor
    total
    results { id name email username verified }
  }
}

Typical integration-facing resources are:

Resource Query Write mutation Required scope
Accounts accounts accounts, reactivateAccount query accounts / modify accounts
Teams teams teams query teams / modify teams
Roles roles roles query roles / modify roles
Scopes scopes scopes query scopes / modify scopes
Clients clients clients query clients / modify clients
Keys keys keys query keys / modify keys
Machine-key authentication — keyAuthentication Scopes assigned to the key
Policies policies policies query policies / modify policies
Features, plans, licences features, plans, licenses features, plans, licenses query licenses / modify licenses

Plural write mutations accept inputs and return the affected IDs. For example, creating or updating a client uses clients(inputs: [ClientInput!]!). The full input, filter, sort, and return types are available through GraphQL introspection in the API explorer. Build against the schema rather than relying on fields not explicitly selected in an example.


Scope and tenant design

Scopes are lowercase names such as query teams or modify accounts. A token is limited to the scopes assigned through the subject’s roles or key, further constrained by the client and optionally by the limits argument during authentication. Request only the scopes needed for the immediate task.

aud is also part of authorization: having a scope does not grant access to every team. The server limits returned records and mutations to the teams the token can access. When your own service validates a token, enforce both the required scope and the expected team membership.

Never use administrative account, client, role, or key mutations from an untrusted frontend. Route those operations through your backend and give each integration the smallest practical scope set.


Account lifecycle and security operations

Use the following public security mutations to support account flows:

Flow Mutation(s) Notes
Invite a pre-created account invite Requires permission to modify the target account. Sends a single-use link.
Redeem invitation or reset link redeem Returns a short-lived setup-only bearer token.
Verify email requestEmailVerification, verifyEmail The request response intentionally does not reveal whether the account exists.
Password reset requestPasswordReset, redeem Do not expose account-existence details to the user.
Account recovery requestAccountRecovery, redeem Requires the saved recovery code plus the delivered link.
Register a passkey webRegistrationOptions, webRegister Run the WebAuthn ceremony in a browser and submit its JSON result.

setup and resetPassword remain for older integrations but are deprecated. New integrations should use redeem, which supports password, passkey, and provider setup consistently.


Production checklist

  • Use HTTPS and the production API base URL provided by Eyemagnet.
  • Store the client secret, key secret, and refresh tokens in a secret manager or encrypted server-side store.
  • Mint v2 JWTs and verify RS256 tokens using JWKS and kid.
  • Check signature, expiry, issuer, required scope, and expected team on every authorization decision.
  • Use short bearer-token lifetimes and refresh safely; never log tokens, passwords, recovery codes, or client/key secrets.
  • Handle GraphQL errors and throttling explicitly.
  • Test client/key rotation and user reauthentication before production launch.

For credentials, scope grants, environment URLs, or support, contact your Eyemagnet integration administrator.