DocuStore.io
Integration

Authentication

Configuring Duar for workspace-scoped authorization with entity-level permissions.

DocuStore uses Duar for authorization. Duar operates in AuthZ mode -- the application authenticates users via an Identity Provider (IdP) directly, and Duar issues short-lived authorization tokens that encode workspace membership and role. Entity-level permissions (visibility, shares) and workspace actions are checked against Duar at request time.

Architecture

                 ┌──────────┐
                 │   IdP    │  (Google, Entra ID, etc.)
                 └────┬─────┘
                      │ IdP token
                      ▼
┌──────────┐    ┌──────────────┐    ┌──────────┐
│  Client  │───▶│ Mint route   │───▶│   Duar   │
│ (Portal) │    │ (service key)│    │  (AuthZ) │
└────┬─────┘    └──────────────┘    └────┬─────┘
     │                                   │ AuthZ token
     │◀──────────────────────────────────┘ (workspace_id, user_id, role)
     │
     │  Authorization: Bearer <IdP token>
     │  X-Authz-Token: <AuthZ token>
     ▼
┌──────────────┐
│  DocuStore   │
│     API      │
└──────────────┘

Flow

  1. The frontend authenticates the user with the configured IdP (e.g., Google OAuth).
  2. The frontend sends the IdP token and the chosen workspace to a mint route. Minting requires the Duar service key, so the browser never calls Duar for this directly: the portal posts to its own /api/auth/mint route, and non-browser clients (the CLI) post to the API's POST /auth/mint. Both forward to Duar's /authz/resolve with the service key.
  3. Duar validates the IdP token, resolves the user's workspace membership and role, and issues an authorization token.
  4. All subsequent API requests send the IdP token in Authorization: Bearer <token> and the authorization token in X-Authz-Token: <token>.
  5. The DocuStore API validates both tokens with the duar-auth SDK (signature, audience, expiry, and that both belong to the same user) and extracts the RequestAuth context.

Workspace Isolation

Every API request is scoped to the workspace encoded in the authorization token. The RequestAuth object provides:

FieldTypeDescription
workspace_idUUIDActive workspace
user_idUUIDAuthenticated user
workspace_rolestringRole in the active workspace
is_adminbooltrue for workspace admins and owners

List endpoints automatically filter by workspace_id. Resource endpoints verify that the requested entity belongs to the user's workspace before returning data.

Entity-Level Permissions

Individual artifacts support fine-grained access control beyond workspace membership:

Visibility

LevelAccess
workspaceAll workspace members can view
privateOnly the owner can view

Sharing (below) grants access to specific users or groups on top of either level.

Sharing

Artifact owners and workspace admins can share artifacts with specific users or groups:

# Share an artifact with a user
curl -X POST http://localhost:8000/artifacts/{id}/shares \
  -H "Authorization: Bearer <idp_token>" -H "X-Authz-Token: <authz_token>" \
  -H "Content-Type: application/json" \
  -d '{"grantee_type": "user", "grantee_id": "user-uuid", "permission": "view"}'

# Update visibility
curl -X PATCH http://localhost:8000/artifacts/{id}/visibility \
  -H "Authorization: Bearer <idp_token>" -H "X-Authz-Token: <authz_token>" \
  -H "Content-Type: application/json" \
  -d '{"visibility": "private"}'

# View current ACL
curl http://localhost:8000/artifacts/{id}/permissions \
  -H "Authorization: Bearer <idp_token>" -H "X-Authz-Token: <authz_token>"

Permission Caching

Duar permission checks are cached for DUAR_CACHE_TTL seconds (default 120) to reduce latency on repeated access to the same resources.

Workspace Actions

Some operations also require a workspace-level RBAC action, granted to roles in Duar. Workspace admins and owners bypass these checks.

ActionRequired for
artifacts:createUploading or creating artifacts, and ingesting literature
artifacts:hileditCorrecting artifact metadata (PATCH /artifacts/{id}/metadata), correcting page compound mentions, and exporting CSER training data
artifacts:deleteDeleting an artifact, removing pages from one, or deleting a page (also needs edit permission on the artifact)

DocuStore registers its actions with Duar at startup. A user without the action gets 403.

Configuration

VariableDefaultDescription
DUAR_URLhttp://localhost:9003Duar service URL
DUAR_SERVICE_KEY(empty)Service-to-service key, generated in the Duar admin panel. Also required for the /auth/mint route
DUAR_SERVICE_NAMEdocu-storeService name registered in Duar. Authorization tokens minted for another service are rejected
DUAR_IDP_JWKS_URLhttps://www.googleapis.com/oauth2/v3/certsIdP JWKS endpoint for token validation
DUAR_IDP_AUDIENCE(empty)Required. Your OAuth client ID; the IdP token's aud must match it
DUAR_IDP_ISSUERhttps://accounts.google.comExpected iss of the IdP token
DUAR_CACHE_TTL120Permission cache TTL in seconds (0 to disable)

SDK Integration

The backend uses the duar-auth Python package:

from duar_auth import RequestAuth

# In FastAPI route handlers, RequestAuth is injected via dependency
@router.get("/artifacts")
async def list_artifacts(
    auth: Annotated[RequestAuth, Depends(get_auth)],
):
    # auth.workspace_id, auth.user_id, auth.is_admin are available
    # auth.can(), auth.accessible(), auth.check_action() for permission checks
    # auth.share(), auth.unshare(), auth.update_visibility() for ACL
    # auth.get_enriched_resource_acl() for full ACL with user profiles
    ...

The frontend uses the @duar-auth/js package (the DuarAuthz client: IdP login, token minting, refresh, and request headers) and the @duar-auth/react package for client-side auth state.

Development Setup

Duar is a separate service and is not part of DocuStore's Docker Compose stack. Run it yourself (see the Duar repository), register a docu-store service app in its admin panel, and point DocuStore at it. The default configuration uses Google as the IdP, but any OIDC-compliant provider can be configured by changing DUAR_IDP_JWKS_URL, DUAR_IDP_AUDIENCE, and DUAR_IDP_ISSUER.

DUAR_URL=http://localhost:9003
DUAR_SERVICE_KEY=sk_...                # from the Duar admin panel
DUAR_SERVICE_NAME=docu-store
DUAR_IDP_AUDIENCE=<your-oauth-client-id>