data-fair-session
- Repo stars 0
- Author updated Live
- Author repo skills-registry
- Domain
- Data
- Compatible agents
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- Trust score
- 88 / 100 · community maintained
- Author / version / license
- @tomevault-io · no license declared
- Token usage
- Lean
- Setup complexity
- Manual integration
- External API key
- Required · Vendor-specific
- Operating systems
- Windows · Docker
- Runtime requirements
- Node.js · Docker
- Permissions
-
- Read-only
- Write / modify
- Network behavior
- External requests
- Install commands
- 26 variants
Profile is derived at build time from SKILL.md and install vectors. Subject to drift from author intent.
Heads up: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: data-fair-session
description: How to use the @data-fair/lib session management system in services that consume sessions (not l…
category: data
runtime: Node.js / Docker
---
# data-fair-session output preview
## PART A: Task fit
- Use case: How to use the @data-fair/lib session management system in services that consume sessions (not login/account management). Use this skill whenever the task involves reading user identity, checking permissions, protecting routes, accessing account/organization info, or implementing authorization logic in a data-fair service -- both on the Express/Node backend and in Vue frontend components. Also use it when the user mentions session middleware, account roles, admin mode, or organization switching in a data-fair context. Use when this capability is needed..
- Inputs: target material, constraints, expected output, and acceptance criteria.
- Evidence boundary: follow “Architecture Overview / Key Types / Express Backend” and do not present inference as author intent.
## PART B: Execution result
- **01** The card summarizes the use case; runtime output centers on “How to use the @data-fair/lib session management system in services that consume sessions (not login/account management). Use this skill whenever the task involves reading user identity, checking permissions, protecting routes, accessing account/organization info, or implementing authorization logic in a data-fair service -- both on the Express/Node backend and in Vue frontend components. Also use it when the user mentions session middleware, account roles, admin mode, or organization switching in a data-fair context. Use when this capability is needed.”.
- **02** When the source has headings, the agent prioritizes “Architecture Overview / Key Types / Express Backend” so the result follows the author’s structure.
- **03** Typical output includes task judgment, concrete steps, required commands or file edits, validation, and follow-up options.
- **04** Risk context follows the fingerprint: read files, write/modify files; may access external network resources; requires Vendor-specific API keys.
## Running Rules
- read files, write/modify files; may access external network resources; requires Vendor-specific API keys.
- Validate with a small sample before expanding scope.
- Return the result, validation criteria, and next iteration options. The source does not require a stable slash command. After installation, invoke the skill by name and describe the task.
Name target files or source material, expected output, forbidden changes, and whether network or shell access is allowed. Permission fingerprint: read files, write/modify files.
Start with a small task and check whether the result follows “Architecture Overview / Key Types / Express Backend”. Inspect diffs, logs, previews, or tests before expanding scope.
Confirm the final output includes a concrete result, evidence, and next action. If it stays generic, tighten inputs, boundaries, and acceptance criteria.
---
name: data-fair-session
description: How to use the @data-fair/lib session management system in services that consume sessions (not l…
category: data
source: tomevault-io/skills-registry
---
# data-fair-session
## When to use
- How to use the @data-fair/lib session management system in services that consume sessions (not login/account managemen…
- Use it when the task has clear inputs, repeatable steps, and validation criteria.
## What to provide
- Target material, scope, expected result, and forbidden changes.
- Whether network, commands, file writes, or external services are allowed.
## Execution rules
- Organize steps around “Architecture Overview / Key Types / Express Backend” and keep inference separate from source facts.
- read files, write/modify files; may access external network resources; requires Vendor-specific API keys.
- Validate with a small sample before expanding the task.
## Output requirements
- Return the deliverable, key evidence, validation method, and next action.
- Mark missing information as unknown; do not invent commands, platforms, or dependencies. The author source anchors workflow facts; repository files anchor sources and commands; Fluxly only adds fit, limitations, and quality judgment.
skill "data-fair-session" {
input -> user goal + target files + boundaries + acceptance criteria
context -> Architecture Overview / Key Types / Express Backend
rules -> SKILL.md triggers / order / output contract
runtime -> Node.js / Docker | read files, write/modify files | may access external network resources
guardrails -> requires Vendor-specific API keys + small-sample validation + diff/log review
output -> copyable result + checklist + next iteration
} data-fair Session Management -- Consumer Guide
This skill covers how services consume sessions produced by Simple Directory (the identity provider). It does NOT cover login flows or account management -- only how a service reads, verifies, and uses session data for authentication and authorization.
Architecture Overview
Sessions are stateless JWT cookies set by Simple Directory. Consuming services never store sessions -- they verify and read them on every request. The JWT is split across two cookies for security: id_token (readable by JavaScript, contains header+payload) and id_token_sign (httpOnly, contains the signature).
Additional cookies carry context: id_token_org (active organization), id_token_dep (active department), id_token_role (switched role), i18n_lang (language).
Key Types
// The central session object -- always present, even for anonymous users
interface SessionState {
user?: User // present if authenticated
organization?: OrganizationMembership // active org (if user switched to one)
account?: Account // derived: who is currently acting
accountRole?: string // role in the active account
lang: string // always present, defaults to 'fr'
}
// Authenticated variant -- user, account, accountRole guaranteed non-null
type SessionStateAuthenticated = SessionState & Required<Pick<SessionState, 'user' | 'account' | 'accountRole'>>
// The polymorphic "owner" of a resource
interface Account {
type: 'user' | 'organization'
id: string
name: string
department?: string
departmentName?: string
}
// Minimal key for matching ownership (used in permission checks)
type AccountKeys = Pick<Account, 'type' | 'id' | 'department'>
The account field is the key abstraction: it normalizes "who is currently acting" regardless of whether it's a personal user or an organization. Resources are owned by an Account; permission checks compare the session's account against the resource's owner.
The user.adminMode flag indicates a platform super-admin in admin mode -- it bypasses all permission checks.
Express Backend
Package: @data-fair/lib-express
Import paths:
import { session } from '@data-fair/lib-express/session.js'
// or the default export:
import session from '@data-fair/lib-express/session.js'
// Sync accessors and helpers (also re-exported from session.js):
import {
reqSession,
reqSessionAuthenticated,
reqAdminMode,
reqUser,
reqUserAuthenticated,
setReqUser,
setReqSession,
assertAccountRole,
assertAdminMode,
getAccountRole,
isAuthenticated,
} from '@data-fair/lib-express/session.js'
Initialization (at server startup)
import { session } from '@data-fair/lib-express/session.js'
// Point to Simple Directory's internal URL for JWKS key fetching
session.init(config.privateDirectoryUrl)
// Typically: 'http://simple-directory:8080' in Docker
Middleware (applied to routes)
import { session } from '@data-fair/lib-express/session.js'
// Parse session on all API routes (anonymous access allowed)
app.use('/api', session.middleware())
// Require authentication
app.use('/api/private', session.middleware({ required: true }))
// Require super-admin
app.use('/api/admin', session.middleware({ adminOnly: true }))
The middleware parses cookies, verifies the JWT via JWKS, and caches the result on the request object. It also blocks non-GET requests for pseudoSession tokens (limited API key sessions).
Reading Session in Route Handlers
After middleware has run, use the sync accessors:
app.get('/api/data', (req, res) => {
const s = reqSession(req) // SessionState (may be anonymous)
if (s.user) { /* authenticated */ }
})
app.post('/api/data', (req, res) => {
const s = reqSessionAuthenticated(req) // throws 401 if not logged in
// s.user, s.account, s.accountRole are guaranteed present
})
app.delete('/api/admin/thing', (req, res) => {
const s = reqAdminMode(req) // throws 401/403 if not super-admin
})
Permission Checking
The permission model is account-based ownership. Resources have an owner: Account field. Check access with:
import { reqSessionAuthenticated, assertAccountRole, assertAdminMode } from '@data-fair/lib-express/session.js'
// Check the user has 'admin' role on the resource's owner account
app.put('/api/resources/:id', async (req, res) => {
const session = reqSessionAuthenticated(req)
const resource = await db.findById(req.params.id)
assertAccountRole(session, resource.owner, 'admin')
// proceed with update...
})
// Accept multiple roles
assertAccountRole(session, resource.owner, ['admin', 'contrib'])
// Super-admin-only operations
assertAdminMode(session)
// Non-throwing check (returns role string or null)
const role = getAccountRole(session, resource.owner)
if (role === 'admin') { /* can edit */ }
getAccountRole resolution order:
- Not authenticated ->
null user.adminMode->'admin'(super-admin bypass)- Target is
type:'user'matchinguser.id->'admin'(self-ownership) - Match against
session.account->session.accountRole - Otherwise ->
null
Options for getAccountRole / assertAccountRole:
allAccounts: true-- check all user's org memberships, not just the currently active oneacceptDepAsRoot: true-- users in the root org (no department) can access department-scoped resources
Filtering Lists by Ownership
A common pattern for listing resources scoped to the current account:
app.get('/api/resources', async (req, res) => {
const session = reqSessionAuthenticated(req)
const query: any = {}
if (req.query.showAll === 'true') {
// Only super-admins can see all resources
assertAdminMode(session)
} else {
// Scope to current account
query['owner.type'] = session.account.type
query['owner.id'] = session.account.id
if (session.account.department) {
query['owner.department'] = session.account.department
}
}
const results = await db.find(query)
res.json(results)
})
Default Owner on Resource Creation
When creating a resource, default the owner to the session's active account:
app.post('/api/resources', async (req, res) => {
const session = reqSessionAuthenticated(req)
const resource = {
...req.body,
owner: req.body.owner ?? session.account
}
// Verify user has permission on the specified owner
assertAccountRole(session, resource.owner, 'admin')
await db.insert(resource)
})
Ownership Transfer
When changing a resource's owner, check permission on both old and new:
if (patch.owner) {
assertAccountRole(session, resource.owner, 'admin') // can remove from old
assertAccountRole(session, patch.owner, 'admin') // can assign to new
}
Synthetic Sessions (API Keys, Internal Calls)
Use setReqUser or setReqSession to create a pseudo-session from an API key or service-to-service call, bypassing normal cookie parsing:
import { setReqUser } from '@data-fair/lib-express/session.js'
// Create a session from an API key lookup
app.use(async (req, res, next) => {
const apiKey = req.headers['x-api-key']
if (apiKey) {
const keyRecord = await db.apiKeys.findOne({ key: apiKey })
setReqUser(req, keyRecord.user, 'fr', keyRecord.account, keyRecord.role)
}
next()
})
Passing Session to Service Layer
Thread the session state as a parameter to service functions rather than relying on request context:
// router.ts
const session = reqSessionAuthenticated(req)
await updateResource(session, req.params.id, req.body)
// service.ts
export async function updateResource(
sessionState: SessionStateAuthenticated,
id: string,
body: any
) {
const resource = await db.findById(id)
assertAccountRole(sessionState, resource.owner, 'admin')
// ...
}
Vue Frontend
Package: @data-fair/lib-vue
Import paths:
import { createSession, useSession, useSessionAuthenticated, getAccountRole } from '@data-fair/lib-vue/session.js'
import type { Session, SessionAuthenticated, SiteInfo, Account } from '@data-fair/lib-vue/session.js'
Setup -- Plain Vue SPA
// main.ts
import { createSession } from '@data-fair/lib-vue/session.js'
const session = await createSession({
// All options are optional, these are the defaults:
// directoryUrl: '/simple-directory',
// sitePath: '',
// defaultLang: 'fr',
})
const i18n = createI18n({ locale: session.state.lang })
createApp(App)
.use(session) // provides session via Vue's provide/inject
.use(i18n)
.mount('#app')
Setup -- Nuxt 3 SSR
Server-side (Nitro plugin):
// server/plugins/session.ts
import { SessionHandler } from '@data-fair/lib-node/session.js'
export const session = new SessionHandler()
export default defineNitroPlugin(async () => {
const config = useRuntimeConfig()
session.initJWKS(config.privateDirectoryUrl)
})
Client-side (Nuxt plugin):
// plugins/session.ts
import { createSession } from '@data-fair/lib-vue/session.js'
export default defineNuxtPlugin(async (app) => {
app.vueApp.use(await createSession({
req: app.ssrContext?.event.node.req, // pass request for SSR cookie reading
route: useRoute(),
}))
})
Auto-imports in nuxt.config.ts:
imports: {
presets: [{
from: '@data-fair/lib-vue/session.js',
imports: ['useSession', 'useSessionAuthenticated']
}]
}
Using Session in Components
useSession() -- returns Session with possibly-undefined user. Use for public-facing pages:
<script setup>
const session = useSession()
</script>
<template>
<div v-if="session.user.value">
Logged in as {{ session.user.value.name }}
<button @click="session.logout()">Logout</button>
</div>
<div v-else>
<button @click="session.login()">Login</button>
</div>
</template>
useSessionAuthenticated() -- returns SessionAuthenticated where user, account, accountRole are guaranteed. Use for protected pages (throws if not logged in):
<script setup>
const session = useSessionAuthenticated()
// Access current account
const accountType = session.state.account.type // 'user' | 'organization'
const accountId = session.state.account.id
const role = session.state.accountRole // 'admin' | 'contrib' | 'user'
</script>
Client-Side Permission Checks
<script setup>
import { getAccountRole } from '@data-fair/lib-vue/session.js'
const session = useSessionAuthenticated()
// Check role for a specific resource owner
const canEdit = computed(() => {
return getAccountRole(session.state, resource.value.owner) === 'admin'
})
// Super-admin check
const isSuperAdmin = computed(() => !!session.state.user?.adminMode)
</script>
Organization Switching
<script setup>
const session = useSessionAuthenticated()
// List available accounts (personal + organizations)
const accounts = computed(() => {
const items = [{ label: session.state.user.name, value: null }]
for (const org of session.state.user.organizations) {
items.push({
label: org.department ? `${org.name} / ${org.departmentName}` : org.name,
value: org.department ? `${org.id}:${org.department}` : org.id
})
}
return items
})
function onSwitch(value: string | null) {
if (!value) {
session.switchOrganization(null)
} else {
const [org, dep] = value.split(':')
session.switchOrganization(org, dep)
}
}
</script>
Session Properties Reference
The Session object returned by useSession():
| Property | Type | Description |
|---|---|---|
state |
reactive(SessionState) |
The raw reactive state object |
user |
ComputedRef<User | undefined> |
Current user (null if anonymous) |
organization |
ComputedRef |
Active organization membership |
account |
ComputedRef<Account | undefined> |
Active account (user or org) |
accountRole |
ComputedRef<string | undefined> |
Role in active account |
lang |
ComputedRef<string> |
Current language |
theme |
Ref<Theme> |
Current theme |
site |
Ref<SiteInfo | null> |
Site info (colors, auth mode) |
| Method | Description |
|---|---|
login(redirect?) |
Navigate to Simple Directory login |
logout(redirect?) |
Delete auth cookies and redirect |
switchOrganization(orgId, dep?, role?) |
Switch active organization |
switchLang(lang) |
Change language (triggers page reload) |
keepalive() |
Refresh the JWT token |
Keepalive runs automatically every 10 minutes on non-iframe top windows. Changing account, lang, or dark triggers a full page reload to ensure data consistency.
Common Patterns Summary
- Express init:
session.init(directoryUrl)at startup - Express middleware:
session.middleware()on route groups - Read session:
reqSession(req)orreqSessionAuthenticated(req)(sync, after middleware) - Check permission:
assertAccountRole(session, resource.owner, 'admin') - Super-admin gate:
assertAdminMode(session) - List filtering: scope queries to
session.account.{type, id, department} - Default owner:
body.owner ?? session.account - Vue setup:
createSession({})as Vue plugin - Vue access:
useSession()for public pages,useSessionAuthenticated()for protected pages - Vue permission:
getAccountRole(session.state, owner)for conditional UI
Source: data-fair/lib — distributed by TomeVault.
Decide Fit First
Design Intent
How To Use It
Boundaries And Review