Skip to main content

Authentication

Imagor Studio uses JWT (JSON Web Token) based authentication. All API calls — both REST and GraphQL — require a valid Bearer token in the Authorization header, except for the auth endpoints themselves.

Auth Endpoints

All auth endpoints are under /api/auth/.

Login

POST /api/auth/login
Content-Type: application/json

{
"username": "alice",
"password": "your-password"
}

Response:

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 604800,
"user": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"displayName": "Alice",
"username": "alice",
"role": "admin"
}
}
FieldDescription
tokenJWT Bearer token to use in subsequent requests
expiresInToken lifetime in seconds (default: 604800 = 7 days)
user.roleadmin, user, or guest

Refresh Token

Exchange a valid (non-expired) token for a new one with a fresh expiry:

POST /api/auth/refresh
Content-Type: application/json

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response: Same shape as the login response, with a new token and expiresIn.

Guest Login

If guest mode is enabled, unauthenticated users can obtain a read-only token:

POST /api/auth/guest

Response: Same shape as login, with role: "guest" and read-only scopes.

Returns 403 Forbidden if guest mode is not enabled.


Using the Token

Include the token as a Bearer token in the Authorization header on every request:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

GraphQL Example

curl -X POST http://localhost:8000/api/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{"query": "{ imagorStatus { configured mode } }"}'

JavaScript / Fetch Example

const response = await fetch('/api/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({ query: '{ imagorStatus { configured } }' }),
})

Roles and Permissions

RoleScopesCan Do
adminread, write, adminEverything: configure imagor, manage users, edit images
userread, writeBrowse gallery, edit images, generate URLs, save templates
guestreadBrowse gallery only (read-only)

Permission Levels in the API

The GraphQL resolvers enforce three permission levels:

  • RequireEditPermission — requires write scope (user or admin). Used by generateImagorUrl, generateImagorUrlFromTemplate, saveTemplate, etc.
  • RequireAdminPermission — requires admin scope. Used by configureEmbeddedImagor, configureExternalImagor, user management, etc.
  • Read-only queries — accessible to all authenticated users including guests.

Token Expiry

Tokens expire after the configured duration (default 7 days). The expiresIn field in the login/refresh response tells you the lifetime in seconds.

To keep a session alive, call /api/auth/refresh before the token expires. The refresh endpoint validates the existing token and issues a new one — it does not require re-entering credentials.

// Example: refresh token 1 hour before expiry
const REFRESH_BUFFER_MS = 60 * 60 * 1000 // 1 hour

function scheduleRefresh(token, expiresIn) {
const refreshIn = (expiresIn * 1000) - REFRESH_BUFFER_MS
setTimeout(async () => {
const res = await fetch('/api/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
})
const { token: newToken, expiresIn: newExpiry } = await res.json()
scheduleRefresh(newToken, newExpiry)
}, refreshIn)
}

Error Responses

HTTP StatusMeaning
400 Bad RequestMissing or invalid request body
401 UnauthorizedMissing, invalid, or expired token
403 ForbiddenToken valid but insufficient permissions
409 ConflictUsername already exists (registration)

Authentication errors return a JSON body:

{
"error": "LOGIN_FAILED",
"message": "Invalid credentials"
}
Generic Error Messages

Login failures always return the same generic LOGIN_FAILED error regardless of whether the username doesn't exist or the password is wrong. This prevents username enumeration attacks.