Skip to content

sutandojs/keeper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

@sutando/keeper

πŸ›‘οΈ A lightweight authentication & API token plugin for Sutando ORM, inspired by Laravel Sanctum.

@sutando/keeper provides sessionless, token-based authentication for modern applications including SPAs, mobile clients, and traditional backends.


✨ Features

  • πŸ” Personal access tokens scoped to users
  • βš™οΈ Extensible and database-agnostic
  • 🧩 Compatible with any Sutando Model (e.g. User)

πŸ“¦ Installation

npm install @sutando/keeper

Or using pnpm:

pnpm add @sutando/keeper

Auto create migration file and run migration

sutando migrate:publish @sutando/keeper
sutando migrate:run
Alternatively, you can create the token table manually
await sutando.connection().schema.createTable('personal_access_tokens', (table) => {
  table.increments('id');
  table.string('tokenable_type').index();
  table.integer('tokenable_id').index();
  table.string('name');
  table.string('token', 64).unique();
  table.string('abilities').nullable();
  table.datetime('last_used_at').nullable();
  table.datetime('expires_at').nullable();
  table.timestamps();

  table.index(['tokenable_type', 'tokenable_id'], 'tokenable_index');
});

πŸ” Usage

Setup

import { HasApiTokens, PersonalAccessToken } from '@sutando/keeper'
import { sutando, Model } from 'sutando'

class User extends HasApiTokens()(Model) {
  // your model definition
}

Issue Token

const user = await User.query().find(1);
const token = await user.createToken('mobile-app');

// Issue token with abilities
const token = await user.createToken('admin', ['read', 'write']);

// Issue token with expiration date
const token = await user.createToken(
  'mobile-app', ['read', 'write'], new Date(Date.now() + 7 * 86400000);
);

Validate Token

const user = await User.findByToken(tokenString);

if (user.tokenCan('read')) {
  // Access granted
}

Revoking Tokens

// Revoke all tokens...
await user.tokens().delete();

// Revoke the token that was used to authenticate the current request...
await user.currentAccessToken().delete();

// Revoke a specific token...
await user.tokens().where('id', tokenId).delete();

πŸ§ͺ Example with Hono

import User from './models/user'
import { bearerAuth } from 'hono/bearer-auth'

const auth = (ability?: string) =>
  bearerAuth({
    verifyToken: async (token, c) => {
      const user = await User.findByToken(token)
      if (!user || (ability && user.tokenCant(ability))) {
        return false
      }
      c.set('user', user)
      return true
    },
  })

app.post('/tokens/create', async (c) => {
  const user = await User.query().find(1)
  const token = await user.createToken('mobile-app')
  return c.json({ token: token.plainTextToken })
})

app.get('/api/user', auth(), async (c) => {
  const user = c.get('user')
  return c.json(user)
})

app.get('/admin', auth('write'), handler)

πŸ“Œ API Reference

HasApiTokens(options)

  • accessTokenModel: Model used for token storage (optional, default: PersonalAccessToken)
  • token_prefix: Prefix for token string (optional, default: '')
  • type: Token type (optional, default: Model name)
  • separator: Separator for returned token string (optional, default: |)

PersonalAccessToken

  • personalAccessToken.findToken(token: string): Promise<PersonalAccessToken | null>
  • personalAccessToken.can(ability: string): boolean
  • personalAccessToken.cant(ability: string): boolean

NewAccessToken

  • newAccessToken.accessToken: PersonalAccessToken
  • newAccessToken.plainTextToken: string

User Model Extensions

  • createToken(name: string, abilities?: string[], expires_at?: Date | string): Promise<NewAccessToken>
  • findByToken(token: string, last_used_at?: Date | string): Promise<User | null>
  • tokenCan(ability: string): boolean
  • tokenCant(ability: string): boolean

πŸ” Security Notes

All tokens are stored as SHA-256 hashes, ensuring they cannot be reverse-engineered if leaked.


πŸ“„ License

MIT Β© Kidd Yu

About

Lightweight API token authentication plugin for Sutando ORM

Resources

Stars

Watchers

Forks

Packages

No packages published