Guides

Authorization

Explain the authz contract, permissions data, and access checks.

Authorization can be implemented with ACL, RBAC, ABAC, or a custom rule system.

Ginjou stays agnostic by exposing one authz contract for permissions data and access checks.

Authz Context

Authz context is the entry point for permissions and access checks.

It provides one authz object to the app.

Interface

Authz
interface Authz {
    getPermissions?: (params?: any) => Promise<unknown | null>

    access?: (params: {
        action: string
        resource?: string
        params?: {
            id?: string | number
            [key: string]: any
        }
        meta?: Record<string, any>
    }) => Promise<{
        can: boolean
        reason?: string
    }> | {
        can: boolean
        reason?: string
    }
}

Methods

MethodRequiredWhat it does
getPermissionsNoReturn the current authorization data, such as roles, scopes, or permission strings.
accessNoReturn one access decision for one action on one resource.

A simple app can start with local arrays or mock logic and switch to a real backend or policy engine later.

<script setup lang="ts">
import { defineAuthz } from '@ginjou/core'
import { defineAuthzContext } from '@ginjou/vue'

const permissions = ['posts.read', 'posts.edit']

defineAuthzContext(defineAuthz({
    async getPermissions() {
        return permissions
    },
    async access({ action, resource }) {
        return {
            can: permissions.includes(`${resource}.${action}`),
        }
    },
}))
</script>

Permissions

usePermissions wraps authz.getPermissions.

Use it when the page needs current authorization data, such as roles, scopes, or permission strings.

It returns a TanStack useQuery result.

FieldMeaning
dataCurrent permissions data returned by authz.getPermissions.
isPendingWhether the permissions query is still loading.
refetchRe-run the permissions query.

If the provider does not define getPermissions, there is no permissions query to run.

<script setup lang="ts">
import { usePermissions } from '@ginjou/vue'
import { computed } from 'vue'

const { data } = usePermissions<string[]>()

const canEdit = computed(() => data.value?.includes('editor') ?? false)
</script>

<template>
    <form>
        <input :readonly="!canEdit">
        <textarea :readonly="!canEdit" />
    </form>
</template>

Access

useCanAccess wraps authz.access.

Use it when the page needs one access decision for one action on one resource.

It returns a TanStack useQuery result.

FieldMeaning
data?.canWhether the action is allowed.
data?.reasonOptional reason from the provider.

Its input shape is built from these fields.

InputMeaning
actionThe action to check.
resourceThe resource name for the decision.
paramsExtra params for the decision, such as id.
metaExtra provider-specific metadata.

useCanAccess expects authz context to exist.

It also needs the provider to define access. If access is missing, there is no access query to run.

<script setup lang="ts">
import { useCanAccess } from '@ginjou/vue'
import { computed } from 'vue'

const { data } = useCanAccess({
    action: 'edit',
    resource: 'posts',
    params: {
        id: 1,
    },
})

const canEdit = computed(() => data.value?.can ?? false)
</script>

<template>
    <form>
        <input :readonly="!canEdit">
        <textarea :readonly="!canEdit" />
    </form>
</template>
Copyright © 2026