Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Server Auth Otp

ASecurity

How to use @owlmeans/server-auth-otp — email OTP AuthPlugin and OtpService. Use when wiring passwordless email login in an OwlMeans server context. Applies to files matching **/context.ts, **/app/auth/*, **/services/otp*.

3 stars
0 votes
0 copies
0 views
Added 9/22/2026
testinggogitapi

Works with

cliapi

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add owlmeans/common --skill server-auth-otp --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Server Auth Otp?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Server Auth Otp
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/owlmeans-server-auth-otp/badge)](https://www.skillsdirectory.com/skills/owlmeans-server-auth-otp)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: server-auth-otp
description: "How to use @owlmeans/server-auth-otp — email OTP AuthPlugin and OtpService. Use when wiring passwordless email login in an OwlMeans server context. Applies to files matching **/context.ts, **/app/auth/*, **/services/otp*."
metadata:
  applyTo: "**/context.ts, **/app/auth/*, **/services/otp*"
---

# Using `@owlmeans/server-auth-otp`

**Install:** `"@owlmeans/server-auth-otp": "^0.1.18-rc.35"` in `dependencies`

Email OTP authentication plugin for the OwlMeans auth-manager plugin system. Relies on `@owlmeans/auth-otp` for the OTP service interface, a Redis resource for code storage, and a `MailerService` to send codes.

## `@owlmeans/auth-otp` — the contracts

`@owlmeans/auth-otp` has no skill of its own because it is the contracts half of this pair. Its
`OtpService` issues an opaque issuance id, persists and mails a code, and verifies that exact
issuance; it also owns `OTP_SERVICE`, `OTP_AUTH_TYPE` (`'email-otp'`), `OTP_RESOURCE`,
`OTP_TTL_SECONDS` (600), `OTP_CODE_LENGTH` (6), and the five-attempt policy. The server package
provides the challenge stores, throttles, mailer integration and plugin.

Depend on it from a shared package that must name the auth type or type the service, and depend on
`@owlmeans/server-auth-otp` only where the server wires itself up — the same producer/consumer split
every other contracts-and-driver pair in the framework uses. Add a constant or a method signature to
the contracts package, never to the implementation.

## Public API surface

| Symbol | Kind | Purpose |
|--------|------|---------|
| `makeOtpService(alias?)` | fn | Service factory — stores/verifies OTP codes |
| `appendOtpPlugin(context)` | fn | Registers the OTP `AuthPlugin` into the server-auth plugin registry |
| `OTP_SERVICE` | const | `'auth-otp-service'` — the service alias `makeOtpService` registers under |
| `OTP_AUTH_TYPE` | const | `'email-otp'` — the auth type string to pass in `init` requests |
| `OTP_RESOURCE` | const | Redis resource alias for code storage |
| `OTP_TTL_SECONDS` | const | Code TTL (600 s = 10 min) |
| `OTP_CODE_LENGTH` | const | 6 |
| `SERVER_AUTH_OTP` | const | `'server-auth-otp'` — this package's own alias |
| `OtpConfig`, `OtpContext` | type | The `cfg.otp` overrides below, as a server config/context |

## Registration requirements

```ts
import { makeOtpService, appendOtpPlugin, OTP_RESOURCE } from '@owlmeans/server-auth-otp'
import { makeRedisResource } from '@owlmeans/redis-resource'
import { makeDefaultConsoleMailerService, MAILER_SERVICE } from '@owlmeans/mailer'
import { makeMailgunMailerService } from '@owlmeans/server-mailer-mailgun'

// 1. Register the Redis code-cache resource.
context.registerResource(makeRedisResource(OTP_RESOURCE))

// 2. Register a MailerService under MAILER_SERVICE (console for dev/tests, Mailgun for prod).
context.registerService(makeDefaultConsoleMailerService())
// or:
context.registerService(makeMailgunMailerService(MAILER_SERVICE))

// 3. Register the OTP service (reads from Redis + Mailer).
context.registerService(makeOtpService())

// 4. Register the OTP AuthPlugin into the auth-manager plugin registry.
appendOtpPlugin(context)
```

## Auth flow

**Init** — client sends `{ type: 'email-otp', userId: 'user@email.com' }`:
- OTP service generates a 6-digit code, stores it in Redis with 10 min TTL, emails it.
- Returns an opaque issuance id in a signed envelope. The id never contains the email or code.

**Authenticate** — client sends `{ challenge: <signed-envelope>, userId: email, credential: '123456', type: 'email-otp', role: AuthRole.User, scopes: [ALL_SCOPES] }`:
- Envelope is opened → the plugin verifies the opaque issuance and code atomically. Five failed
  attempts invalidate it; one concurrent correct verification can consume it.
- `IdentityLinkingService` finds the linked profile, or links this email to the person's platform
  identity — registering an account, a profile and an organization entity only when the address is
  new to the platform.
- Copies `userId`, `profileId`, `entitySlug`, `role` and `scopes` from the resolved payload onto the
  credential, sets `credential.type = AuthenticationType.OneTimeToken`, and returns the signed auth
  token.
- `type`, `role`, `scopes` are required by the shared `AuthCredentialsSchema` (spread from
  `AuthPayloadSchema.required`) even though the OTP plugin overwrites `role`/`scopes`/`type` on
  success — a caller that omits them never reaches the plugin at all (see Gotchas).

## Config overrides (optional)

Pass `ctx.cfg.otp` to override defaults:

```ts
cfg.otp = {
  mailerAlias: 'my-mailer',       // default: MAILER_SERVICE ('mailer-service')
  resourceAlias: 'my-otp-cache',  // default: OTP_RESOURCE  ('auth-otp-cache')
  identityAlias: 'my-identity',   // default: AUTH_IDENTITY_LINKING
}
```

## Rules

- Always register the Redis challenge store, Redis throttle service, and mailer service BEFORE the
  OTP service. Production uses `makeRedisOtpChallengeStore` and `makeRedisThrottleService`; memory
  implementations are local/test defaults only.
- Call `appendOtpPlugin(context)` once per context — it adds to the shared plugin registry singleton.
- `credential.entitySlug` on the authenticate request **selects nothing**. The plugin copies it into
  the linking details as `clientId` (defaulting to `'default'`) and `entityId`, and
  `@owlmeans/server-auth-identity` reads neither: `getLinkedProfile` keys on the external login key
  built from the auth type, the `'email'` service and the address, and `linkProfile` either reuses
  the person's existing platform profile — matched on the account name — or mints a brand-new
  organization entity. Whatever the caller sent is then overwritten with the linked profile's own
  slug before the envelope is signed, so the address alone decides which identity and which
  organization the token names.
- Errors from this plugin are `AuthenFailed` (from `@owlmeans/auth`) — callers catch that, not raw `Error`.
- Use the email throttle for the public integrated-IAM flow: one issuance per minute and ten per
  hour, with a 429 and Retry-After. The companion IP throttle is available for an ingress that
  safely supplies a normalized client address; throttle keys hash email/IP values and never store
  the raw identifier.
- For tests, register `makeDefaultConsoleMailerService()` and read `svc.captured[n].text` to extract
  the code. Bare `makeConsoleMailerService()` registers under `CONSOLE_MAILER` (`'console-mailer'`),
  which is not the alias `makeOtpService` resolves — it looks up `cfg.otp?.mailerAlias ?? MAILER_SERVICE`.

## Gotchas

- **The OTP challenge must be opaque and plugin-owned.** The generic auth-manager replay policy
  does not consume it before the OTP store can count attempts; the plugin atomically owns verify
  and consume. Never encode the email or code in the issuance id, and never replace the bounded
  attempt counter with a bare auth-cache replay key.
- **`AuthCredentialsSchema.credential` has a `minLength` floor** (from `@owlmeans/auth`) sized for
  long tokens/signatures from other plugins (Ed25519 signature, OAuth code). A 6-digit OTP code is
  legitimately shorter — the floor is `minLength: 1` and must stay low enough to admit it, or every
  authenticate call 400s before the plugin ever runs.
- **`scopes`/`role`/`type` are schema-required on the authenticate body**, spread from
  `AuthPayloadSchema.required` into `AuthCredentialsSchema` — even though this plugin overwrites
  all three on success. A caller built without going through `@owlmeans/client-auth`'s
  `AuthenticationControl` (which fills them automatically) must set them explicitly or the request
  fails Fastify schema validation (`FST_ERR_VALIDATION`) before reaching this plugin at all.
- **The resulting auth token can exceed 1024 characters** — it wraps the full `AuthCredentials`
  envelope, including the original allowance challenge, base64-encoded. A consumer route that
  accepts this token (e.g. an OIDC `PROVIDER_INTERACTION` finalizer) must size its own `token`
  field schema accordingly; the generic `AuthTokenSchema` (`maxLength: 1024`) is too small.

## Related

- `@owlmeans/auth-otp` — the `OtpService` interface and the shared constants
- `@owlmeans/mailer` — `MailerService`, the console transport
- `@owlmeans/server-mailer-mailgun` — the production Mailgun transport
- `server-auth` skill — the auth-manager plugin system this plugin registers into
- `server-auth-identity` skill — how the linked profile and its organization entity are stored
- `auth-protocol` skill — error hierarchy and identity read rules

Attribution

owlmeansowlmeans
View sourceMore from owlmeans →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Screen Reader Testing

Practical guide to testing web applications with screen readers for comprehensive accessibility validation.

397921 votes

Tdd Workflow

在编写新功能、修复错误或重构代码时使用此技能。强制执行测试驱动开发,包含单元测试、集成测试和端到端测试,覆盖率超过80%。

2456590 votes

Python Testing

使用pytest、TDD方法、夹具、模拟、参数化和覆盖率要求的Python测试策略。

2456590 votes

Springboot Tdd

使用JUnit 5、Mockito、MockMvc、Testcontainers和JaCoCo进行Spring Boot的测试驱动开发。适用于添加功能、修复错误或重构时。

2456590 votes

Golang Testing

Go测试模式包括表格驱动测试、子测试、基准测试、模糊测试和测试覆盖率。遵循TDD方法论,采用地道的Go实践。

2456590 votes
View all in testing →