@tango-ts/contrib-auth
v0.9.0
Published
Batteries-included auth app for Tango: User and AuthToken models, password hashing, token issuing routes, and a ready-made authentication class.
Downloads
1,308
Readme
@tango-ts/contrib-auth
Responsibility
The batteries-included auth app (Tango's django.contrib.auth +
rest_framework.authtoken): a built-in User model with secure password
hashing, a database-backed opaque AuthToken model, token-issuing routes
(/login/, /logout/, /me/), and a ready-made Authentication class that
plugs into viewsets. It is NOT responsible for the authentication/permission
interfaces (those live in @tango-ts/auth), nor for OAuth/social login or
JWT issuance — those are layers on top.
What it responds to
authRoutes()produces aRouterof WebRequest→Responsehandlers.authTokenAuthentication()fulfills theAuthenticationcontract from@tango-ts/auth(consumed byModelViewSet'sauthenticationoption).app(via@tango-ts/contrib-auth/app) fulfills theTangoAppcontract for the CLI:tango migrate --app node_modules/@tango-ts/contrib-auth/dist/app.jsapplies the shipped migrations.
Functionality
Usermodel (auth_users): email login,pbkdf2_sha256password hashes in Django's exact encoded format,isActive/isStaff/isSuperuserflags that powerIsAdminUser,dateJoined,lastLogin.AuthTokenmodel (auth_tokens): opaquetango_-prefixed bearer tokens. Only the SHA-256 of a token is stored; plaintext is returned once at issue time. Optional expiry (expiresAt) andlastUsedAttracking (throttled to one write per minute per token).createUser/createSuperuser/authenticateUser(timing-safe against user enumeration).tango createsuperuser(in@tango-ts/cli) wrapscreateSuperuserfor bootstrap.issueToken/revokeToken/verifyAuthToken.authRoutes():POST /login/({ email, password }→{ token, user }),POST /logout/(revokes the presented token),GET /me/(anapiViewrunning the standard pipeline).- Plays with project-level auth: pass
authentication: [authTokenAuthentication()]todefineServer/defineProjectandctx.useris set on every route and viewset without per-route wiring. - Shipped migration
0001_initialinmigrations/(packaged with the npm tarball). FKs are declared withdbConstraint: falseso the schema deploys on PlanetScale/Vitess; token verification re-fetches the user row, so deleted/deactivated users lose access without a database cascade.
Design patterns that matter here
- Serverless: no module-level mutable state; every request resolves tokens through the ORM's request-scoped connection.
- Runtime-agnostic crypto: WebCrypto only (
crypto.subtle,crypto.getRandomValues) — nonode:cryptoin the runtime entrypoint. The one Node-specific module (src/app.ts, which resolves the packaged migrations directory) is a separate export (./app) used by the CLI. - Django parity: password hashes use Django's storage format
(
pbkdf2_sha256$<iterations>$<salt>$<b64>), so hashes migrate between Django and Tango projects unchanged. - Secrets are hashed at rest: never store a plaintext token or password;
never put the password hash on
ctx.useror in a response. - Indistinguishable login failures: unknown email, wrong password, and inactive account all return the same 400 with comparable timing.
Public contract
index.ts exports: User, AuthToken, models, publicUser,
hashPassword, verifyPassword, generateToken, hashToken,
createUser, createSuperuser, authenticateUser, issueToken,
revokeToken, verifyAuthToken, authTokenAuthentication, authRoutes,
and their option/row types. ./app exports the TangoApp. Nothing else may
be imported from outside.
Testing
- Unit:
test/hashers.test.ts(round-trips, malformed hashes, the published PBKDF2-HMAC-SHA256 test vector, Django format),test/tokens.test.ts(entropy/format, SHA-256 cross-checked againstnode:crypto). - Integration (real MySQL):
test/contrib-auth.integration.test.tsapplies the shipped migration viamigrateApp, then exercises login → protected viewset →/me/→ logout end to end, plus expiry, deactivation, andIsAdminUserenforcement. - Parity (vs Django/DRF oracle): password hash format is Django's; a hash generated by either side verifies on the other.
