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 } }"}'

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 configureImagor, 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.