Authorization
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
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
| Method | Required | What it does |
|---|---|---|
getPermissions | No | Return the current authorization data, such as roles, scopes, or permission strings. |
access | No | Return 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>
<script lang="ts">
import { defineAuthz } from '@ginjou/core'
import { defineAuthzContext } from '@ginjou/svelte'
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.
| Field | Meaning |
|---|---|
data | Current permissions data returned by authz.getPermissions. |
isPending | Whether the permissions query is still loading. |
refetch | Re-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>
<script lang="ts">
import { usePermissions } from '@ginjou/svelte'
const { data } = usePermissions<string[]>()
const canEdit = $derived(data?.includes('editor') ?? false)
</script>
<form>
<input readonly={!canEdit}>
<textarea readonly={!canEdit}></textarea>
</form>
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.
| Field | Meaning |
|---|---|
data?.can | Whether the action is allowed. |
data?.reason | Optional reason from the provider. |
Its input shape is built from these fields.
| Input | Meaning |
|---|---|
action | The action to check. |
resource | The resource name for the decision. |
params | Extra params for the decision, such as id. |
meta | Extra 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>
<script lang="ts">
import { useCanAccess } from '@ginjou/svelte'
const { data } = useCanAccess({
action: 'edit',
resource: 'posts',
params: {
id: 1,
},
})
const canEdit = $derived(data?.can ?? false)
</script>
<form>
<input readonly={!canEdit}>
<textarea readonly={!canEdit}></textarea>
</form>