GenAuth management SDK
This page explains how to manage users, organizations and permissions through qoni.genauth, including management credentials, pagination, responses and errors.
Unreleased interface
This page describes the GenAuth management extension to the Qoni SDK. As of 2026-10-09, the npm release @qoniai/qoni@0.9.0 does not include these new interfaces. The examples require an SDK build containing the management extension. The installation version will be updated when a release is available. See SDK reference for released delegation interfaces.
The GenAuth management contract uses Authing Node SDK 4.0.2 as its baseline, exposing 35 namespaces and 591 JSON management method names. Of these, 496 come from the official SDK and 95 are GenAuth extensions or compatibility names. Multiple names may map to the same endpoint. Deployment versions, management permissions and feature gates determine availability.
Requirements and initialization
Use Qoni AK/SK on a trusted Node.js server. Confirm that the credential-bound GenAuth user pool enables the management capabilities you need.
import { Qoni } from '@qoniai/qoni'
const qoni = new Qoni({
accessKey: process.env.QONI_ACCESS_KEY!,
secretKey: process.env.QONI_SECRET_KEY!,
})
const { data: users } = await qoni.genauth.users.list({ page: 1, limit: 20 })
console.log(users.list, users.totalCount)The SDK discovers GenAuth through Qoni runtime configuration, exchanges and caches a management token, and shares token exchanges between concurrent calls. Management calls use the AK/SK-bound user pool. They do not require a user access token, delegation token or QONI_USER_ID.
| Option | Type | Description |
|---|---|---|
accessKey / secretKey | string | Qoni management credentials. Use only on a trusted server |
host | string | Optional. Qoni Console / SDK gateway for a private deployment |
genauthHost | string | Optional. Explicit GenAuth address when it differs from runtime discovery |
timeoutMs | number | Timeout per HTTP request; defaults to 30000 |
fetch | typeof fetch | Optional. Custom transport |
AK/SK here means Qoni credentials. When migrating Authing code, do not substitute native Authing management credentials for Qoni AK/SK.
Calling methods
Use a domain method or keep the Authing method name. Both call the same management endpoint:
await qoni.genauth.groups.create({ code: 'engineering', name: 'Engineering' })
// Equivalent call; execute only one when creating the same group
// await qoni.genauth.management.createGroup({ code: 'engineering', name: 'Engineering' })
await qoni.genauth.groups.addMembers({
code: 'engineering',
userIds: ['<genauth-user-id>'],
})All methods accept one input object. Authing positional parameters become named fields, such as management.deleteById({ id: '<id>' }). The SDK handles URL encoding and path binding. Inputs and results have TypeScript types; see the management method index for all methods and type names.
| Parameter | Type | Description |
|---|---|---|
| First argument | Method-specific Input type | Business input object; may be omitted when no fields are required |
Second argument signal | AbortSignal | Optional. Pass { signal } to cancel a request |
adminToken / userPoolId | string | Optional. Supply both to override exchanged management credentials |
users.*
Create, read and update
const { data: user } = await qoni.genauth.users.create({
username: '<unique-username>',
name: 'SDK Demo',
})
const { data: saved } = await qoni.genauth.users.get({ userId: user.userId })
await qoni.genauth.users.update({ userId: saved.userId, nickname: 'Demo' })| Method | Main input | data |
|---|---|---|
list | page, limit, keywords, options | { list, totalCount } |
get | userId | User object |
getBatch | userIds | User array |
create | User fields such as username, email, name | User object |
createBatch | list; compatibility alias users | User array |
update | userId and fields to update | User object |
deleteBatch | userIds | Operation result |
iterate | Pagination and filters | AsyncIterable, yielding individual users |
When both list and users are supplied to createBatch, list takes precedence. User pool settings determine username, email and phone uniqueness and creation rules.
An employed user cannot be deleted directly. Call users.resign({ userId }) only when departure is intended, then delete if required. The SDK never resigns a user automatically after deletion fails. Removing a user's last department may also be rejected by the server.
Pagination and iteration
users.list maps top-level page / limit to options.pagination, preserving other options. Top-level values take precedence when both pagination forms are supplied. management.listUsers uses the original Authing DTO's options.pagination. Other list methods use their own pagination parameters.
const { data: page } = await qoni.genauth.users.list({ page: 1, limit: 20 })
console.log(page.totalCount)
for await (const user of qoni.genauth.users.iterate({ limit: 50 })) {
console.log(user.userId)
// break stops iteration without requesting another page
}| Field | iterate rule |
|---|---|
page | Starting page; defaults to 1; must be a positive integer |
limit | Page size; defaults to 50; range 1–50 |
keywords / options | The same filters are preserved for every page |
{ signal } | Second argument; cancels a request or further iteration |
Paginated lists are not transactional snapshots. User creation or deletion during iteration can affect page contents.
Keywords and advanced filters
const { data: matches } = await qoni.genauth.users.list({
keywords: 'alice',
page: 1,
limit: 20,
options: { fuzzySearchOn: ['username', 'email'] },
})
console.log(matches.list)Search requires a server index
Keyword and advanced-filter queries use the GenAuth search service. In representative live checks on 2026-10-08, unfiltered lists and reads by ID passed, but keyword and advanced-filter queries did not return newly created users. Custom-field keyword queries also returned an upstream error. Verify your deployment's search service and index before relying on these queries. The SDK preserves server results and errors rather than replacing empty results with a local scan.
Roles, resources and authorization
Roles and resources belong to an existing permission namespace. If the user pool has no default namespace, create and pass a business namespace explicitly. Keep namespace codes and application identifiers within 32 characters.
await qoni.genauth.namespaces.create({ code: 'project', name: 'Project' })
await qoni.genauth.roles.create({
namespace: 'project', code: 'reader', name: 'Reader',
})
await qoni.genauth.roles.assign({
namespace: 'project',
code: 'reader',
targets: [{ targetType: 'USER', targetIdentifier: '<genauth-user-id>' }],
})
await qoni.genauth.resources.create({
namespace: 'project',
code: 'documents',
type: 'API',
apiIdentifier: '/documents',
actions: [{ name: 'documents:read', description: 'Read documents' }],
})
await qoni.genauth.permissions.authorize({
namespace: 'project',
list: [{
targetType: 'ROLE',
targetIdentifiers: ['reader'],
resources: [{
code: 'documents:*', resourceType: 'API', actions: ['documents:read'],
}],
}],
})
const { data: permission } = await qoni.genauth.permissions.isAllowed({
namespace: 'project',
userId: '<genauth-user-id>',
resource: 'documents:123',
action: 'documents:read',
})
console.log(permission.allowed)| Field | Used in | Description |
|---|---|---|
namespace | Roles, resources, grants and checks | Shared business permission namespace |
type | resources.create | Resource type; the example uses API |
apiIdentifier | API resource creation | API identifier |
resourceType | Resource items in permissions.authorize | Granted resource type |
actions | Resource definitions and grants | Full action name, such as documents:read |
allowed | isAllowed().data | Server permission decision |
Resource definitions use type; grant items use resourceType. This example creates a role and grants permissions. Read existing business objects before reusing them to avoid duplicate creation.
Custom fields and data
Define a field before setting data on a target object:
await qoni.genauth.customFields.set({
list: [{
targetType: 'USER', key: 'costCenter', label: 'Cost center', dataType: 'STRING',
}],
})
await qoni.genauth.customFields.setData({
targetType: 'USER',
targetIdentifier: '<genauth-user-id>',
list: [{ key: 'costCenter', value: 'engineering' }],
})
const { data: fields } = await qoni.genauth.customFields.list()
console.log(fields)| Method | Purpose | Main parameters |
|---|---|---|
list | Read field definitions | targetType, defaults to USER |
set | Create or update field definitions | targetType, key, label, dataType in list |
setData | Set field values on an object | targetType, targetIdentifier, list |
getData | Read field values on an object | targetType, targetIdentifier |
Supply dataType when creating or updating a definition. An existing field's data type cannot change. Role field data also requires its namespace. Field lists with keyword depend on the search service; see the search limitation above.
Namespaces
All namespaces below belong to qoni.genauth. See the management method index for every method name, alias and type.
| Namespace | Purpose |
|---|---|
users | Users, identity bindings and lifecycle |
roles | Roles and member assignments |
groups / dynamicGroups | Static and dynamic groups |
organizations / departments / positions | Organizations, departments and positions |
applications | Application configuration and access grants |
namespaces / resources / permissions | Permission namespaces, resources and permission checks |
policies / dataPermissions | Policies and data permissions |
tenants | Tenants, members and tenant settings |
identityProviders | External identity providers and connections |
customFields | Custom field definitions and data |
auditLogs / sessions | Audit logs and login sessions |
security / whitelist / riskLists | MFA, security settings, whitelists and risk lists |
webhooks / events | Webhooks and events |
sync / pipelines | Synchronization and authentication flows |
accessKeys | Administrator access credentials |
publicAccounts / invitations | Public accounts and invitations |
metadata / email / billing | Metadata, email and commercial configuration |
devices / deviceRules | Devices and device rules |
approvals / ldap | Approvals and LDAP configuration |
management | Flat entry point for all 591 method names |
management is a flat entry point and is not counted among the 35 domain namespaces. Legacy GraphQL/v2, raw makeRequest, WebSocket helpers and multipart upload are outside this JSON management surface.
Responses, cancellation and errors
Management methods return QoniResponse<T>: business results are in data and request identifiers in meta. users.iterate yields individual user objects.
import { QoniError } from '@qoniai/qoni'
const controller = new AbortController()
try {
const { data, meta } = await qoni.genauth.roles.list(
{ namespace: 'project' },
{ signal: controller.signal },
)
console.log(data.list, meta.requestId)
} catch (error) {
if (error instanceof QoniError) {
console.error(error.status, error.code, error.requestId, error.message)
}
throw error
}Call controller.abort() to cancel a request. If an exchanged management token expires or is rejected by authentication, the SDK refreshes and retries at most once. Permission errors, business errors, timeouts and network failures do not automatically replay writes.
| Condition | SDK behavior | Action |
|---|---|---|
| Missing required fields or incomplete credential override | QoniValidationError | Complete the input |
| Insufficient management permissions | Throws a permission error | Check administrator permissions and feature gates |
HTTP 200 with business statusCode >= 400 | Throws QoniError rather than treating it as success | Correct the business input or state |
Numeric apiCode | Preserved as a string in error.code | Retain the code and request identifiers for diagnosis |
| Upstream or search service failure | Throws the corresponding SDK error; original response in error.body | Check GenAuth; avoid blindly retrying writes |
Explicit management token
If you already have a GenAuth management token, supply adminToken and userPoolId in the same call. They enter authentication headers rather than the business payload. The SDK does not refresh explicitly supplied tokens.
const { data } = await qoni.genauth.roles.list({
namespace: 'project',
adminToken: process.env.GENAUTH_MANAGEMENT_TOKEN!,
userPoolId: process.env.GENAUTH_USERPOOL_ID!,
})
console.log(data.list)Passing only userPoolId does not switch the user pool for token exchange. Set genauthHost during initialization when the target GenAuth address differs from runtime discovery.
Next steps
- Reference: Management method index, SDK reference
- Integration: Integrating with Authing