Skip to content

Plugin Ecosystem ​

UAAA's plugin system allows extending authentication capabilities with custom credential providers and functionality.

Overview ​

Plugins are npm packages that implement the UAAA plugin interface. They can:

  • Register custom credential types
  • Add user claims
  • Extend HTTP endpoints
  • Hook into authentication flows
  • Integrate external identity providers

Built-in Plugins ​

UAAA includes several built-in credential plugins:

password ​

Username/password authentication with bcrypt hashing.

Security Level: MEDIUM Configuration: Password complexity rules, bcrypt rounds

email ​

Email-based OTP (One-Time Password) verification.

Security Level: MEDIUM (2) Configuration: SMTP settings, OTP length, TTL

sms ​

SMS-based OTP verification.

Security Level: HIGH (3) Configuration: SMS gateway (Twilio, etc.), OTP settings

totp ​

Time-based One-Time Password (Google Authenticator).

Security Level: HIGH Configuration: Issuer name, time window

webauthn ​

WebAuthn/FIDO2 hardware key authentication.

Security Level: HIGH (3) Configuration: Relying Party ID, origin, timeout

External Plugins ​

@pku-uaaa/plugin-iaaa ​

Integrates PKU's IAAA (Identity Authentication and Authorization) system.

Features:

  • OAuth token validation
  • Direct credential authentication
  • PKU identity claims mapping
  • Email auto-inference

Installation:

bash
npm install @pku-uaaa/plugin-iaaa

Configuration:

json
{
  "plugins": ["password", "totp", "@pku-uaaa/plugin-iaaa"],
  "iaaaConfig": {
    "id": "app-id",
    "name": "My App",
    "key": "app-secret",
    "redirect": "https://auth.example.com/callback"
  }
}

Plugin Architecture ​

Plugin Interface ​

typescript
interface IPlugin {
  name: string
  version: string
  description?: string
  license?: string
  author?: string
  configType?: ArkType           // Config schema validation
  setup(ctx: PluginContext): void | Cleanup | Promise<void | Cleanup>
}

Plugin Context ​

typescript
class PluginContext {
  app: App                       // Access to application instance
  manager: PluginManager         // Plugin manager
  metadata: IPluginMetadata      // Plugin metadata
}

Credential Interface ​

Credential plugins implement CredentialImpl:

typescript
abstract class CredentialImpl {
  abstract type: CredentialType

  // Availability checks
  showLogin(ctx: CredentialContext): Promise<ICredentialLoginInfo | null>
  showVerify(ctx: CredentialContext, userId: string, targetLevel: SecurityLevel, matchedCredentials: ICredentialDoc[]): Promise<ICredentialVerifyInfo | null>
  showBind(ctx: CredentialContext, userId: string): Promise<ICredentialBindInfo | null>

  // Authentication operations
  login?(ctx: CredentialContext, payload: unknown): Promise<ICredentialLoginResult>
  verify(ctx: CredentialContext, userId: string, targetLevel: SecurityLevel, payload: unknown): Promise<ICredentialVerifyResult>
  bind(ctx: CredentialContext, userId: string, credentialId: string | undefined, payload: unknown): Promise<ICredentialBindResult>
  unbind(ctx: CredentialContext, userId: string, credentialId: string, payload: unknown): Promise<ICredentialUnbindResult>

  // Optional: auto-provisioning
  ensure?(ctx: CredentialContext, payload: unknown): Promise<ICredentialEnsureResult>
}

Plugin Lifecycle ​

  1. Loading: Plugin module imported
  2. Validation: Config schema validated
  3. Setup: setup() function called
  4. Registration: Credentials/claims registered
  5. Ready: Plugin active
  6. Cleanup: Optional cleanup on shutdown

Installing Plugins ​

From npm ​

bash
npm install @org/uaaa-plugin-name

# Add to config
{
  "plugins": ["password", "@org/uaaa-plugin-name"]
}

From Local Path ​

json
{
  "plugins": ["password", "/path/to/local/plugin"]
}

Shorthand Resolution ​

Plugin names are resolved in order:

  1. Exact name: @org/plugin-name
  2. Local path: /path/to/plugin
  3. Shorthand: name → @uaaa/plugin-name
  4. Built-in: builtin/name

Plugin Configuration ​

Plugin-specific configuration is merged into main config:

json
{
  "plugins": ["@org/plugin-custom"],
  "customPluginOption": "value",
  "customPluginConfig": {
    "nested": "option"
  }
}

Plugins validate their configuration using ArkType schemas.

Developing Plugins ​

See Plugin Development Guide for:

  • Creating credential plugins
  • Registering custom claims
  • Adding HTTP endpoints
  • Publishing plugins
  • Best practices

Plugin Examples ​

Simple Credential Plugin ​

typescript
// index.ts
import { definePlugin, CredentialImpl, SECURITY_LEVEL } from '@uaaa/server'

class MyCredImpl extends CredentialImpl {
  readonly type = 'mycred'

  async showLogin(ctx) {
    return { recommended: false }
  }

  async showVerify(ctx, userId, targetLevel, matchedCredentials) {
    return { recommended: true }
  }

  async showBind(ctx, userId) {
    return { securityLevel: SECURITY_LEVEL.MEDIUM }
  }

  async verify(ctx, userId, targetLevel, payload) {
    // Verify user with external API
    const valid = await this.validateExternal(payload.token)
    if (!valid) throw new Error('Invalid token')

    return {
      userId,
      securityLevel: SECURITY_LEVEL.MEDIUM
    }
  }

  async bind(ctx, userId, credentialId, payload) {
    const newCredentialId = await ctx.manager.bindCredential(
      ctx,
      this.type,
      userId,
      credentialId,
      {
        userIdentifier: '',
        globalIdentifier: payload.externalId,
        data: '',
        secret: payload.token,
        remark: '',
        expiration: 0
      }
    )
    return { credentialId: newCredentialId }
  }

  async unbind(ctx, userId, credentialId, payload) {
    await ctx.manager.unbindCredential(ctx, this.type, userId, credentialId)
    return {}
  }
}

export default definePlugin({
  name: 'mycred',
  version: '1.0.0',
  setup: (ctx) => {
    ctx.app.credential.provide(new MyCredImpl(ctx.app))
  }
})

Plugin with Custom Endpoint ​

typescript
export default definePlugin({
  name: 'myplugin',
  version: '1.0.0',
  setup: (ctx) => {
    // Register credential
    ctx.app.credential.provide(new MyCredImpl(ctx.app))

    // Add custom endpoint
    ctx.app.hook('extendApp', (router) => {
      router.get('/api/plugin/myplugin/data', async (c) => {
        return c.json({ data: 'custom data' })
      })
    })

    // Register custom claim
    ctx.app.claim.addClaimDescriptor({
      name: 'myplugin:custom_field',
      description: 'Custom field from plugin',
      securityLevel: SECURITY_LEVEL.LOW,
      editable: false
    })
  }
})

Plugin Discovery ​

Browse plugins:

Plugin Security ​

Best Practices:

  • Validate all inputs with ArkType
  • Use secure credential storage
  • Avoid storing secrets in plain text
  • Log security-relevant events
  • Rate limit external API calls
  • Handle errors gracefully
  • Document security considerations

Plugin Review:

Before installing third-party plugins:

  • Review source code
  • Check npm downloads and stars
  • Verify maintainer identity
  • Test in development first
  • Monitor logs after installation

Next Steps ​

Released under the MIT License.