Modern, lightweight authentication microservice with JWT tokens, session management, and comprehensive test coverage. Fully updated for 2025 standards with Node v25 compatibility.
- π JWT Authentication - ES512 (ECDSA P-521 + SHA-512) tokens via jose
- ποΈ Redis JSON Storage - Fast, schema-less user and session data
- π Session Management - Multi-device support with session refresh and revocation
- π§ Email Verification - Account confirmation with time-limited codes
- π Password Reset - Secure reset flow with rate limiting
- π₯ User Invitations - Passwordless invite system
- π‘οΈ Security - bcrypt hashing, rate limiting, CORS, cookie security
- π§ͺ Test Coverage - 87.63% statements, 76.19% functions (node:test + c8)
- π Production Ready - Docker support, Redis Sentinel, comprehensive error handling
Tech Stack:
- Koa - Web framework
- @hydre/graphql-http - GraphQL server
- Redis JSON - Data storage
- jose - JWT signing and verification
- bcryptjs - Password hashing
- ZeroMQ - Email notifications (optional)
Data Model:
- Users: Stored as JSON documents with email (indexed), password hash, verification status
- Sessions: Device-specific sessions with refresh tokens, user agent tracking
- Tokens: Short-lived JWTs (20min default) with refresh capability
@hydre/auth is a standalone microservice - you run it as a separate server and make GraphQL API calls to it from your application. It is NOT a library you import into your code.
# Clone repository
git clone https://github.com/HydreIO/auth.git
cd auth
# Start auth service + Redis
docker compose up
# Service available at http://localhost:3000# Clone repository
git clone https://github.com/HydreIO/auth.git
cd auth
# Install dependencies
npm install
# Start Redis (via Docker)
docker compose up -d
# Run server
npm startConfigure via environment variables:
# Cryptographic Keys (PKCS#8 format for jose)
PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n..."
PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n..."Generate keys:
# ES512 (ECDSA P-521)
openssl ecparam -genkey -name secp521r1 -noout | openssl pkcs8 -topk8 -nocrypt# Server
PORT=3000
GRAPHQL_PATH=/
SERVER_HOST=localhost
# Redis
REDIS_HOST=localhost
REDIS_USE_SENTINEL=false # Enable for production
REDIS_SENTINEL_PORT=26379
REDIS_MASTER_NAME=mymaster
# JWT
JWT_ISSUER=hydre-auth
JWT_AUDIENCE=hydre-services
ACCESS_TOKEN_EXPIRATION=20m
CONFIRM_ACCOUNT_TOKEN_EXPIRATION=1d
# Cookies
COOKIE_SECURE=true # HTTPS only
COOKIE_SAMESITE=Lax
COOKIE_DOMAIN=.example.com
COOKIE_PATH=/
ACCESS_TOKEN_COOKIE_NAME=virtual-fox
# Security
BCRYPT_ROUNDS=12
MAX_SESSION_PER_USER=10
ALLOW_REGISTRATION=true
# Rate Limiting
RESET_PASS_DELAY=5000 # 5 seconds between password resets
CONFIRM_ACCOUNT_DELAY=5000 # 5 seconds between confirmation codes
# Notifications (optional)
SOCKET_NOTIFIER_ADDRESS=tcp://0.0.0.0:3001
# CORS
ORIGINS=.* # Semicolon-separated regex patternsCreate User:
mutation {
create_user(mail: "user@example.com", pwd: "secure123", lang: EN)
}Login:
mutation {
create_session(mail: "user@example.com", pwd: "secure123", remember: true)
}Get Current User:
query {
me {
mail
verified
member_since
}
}Refresh Token:
mutation {
refresh_session
}Logout:
mutation {
delete_session
}Confirm Account:
mutation {
confirm_account(code: "jwt-code-from-email")
}Password Reset:
mutation {
# Request reset code
create_pwd_reset_code(mail: "user@example.com", lang: EN)
# Reset password with code
update_pwd(code: "jwt-code-from-email", new_pwd: "newsecure123")
}Invite User:
mutation {
invite_user(mail: "invited@example.com")
}# Build image
docker build -t hydre/auth .
# Run with docker-compose
docker compose up# Run tests
npm test
# Run with coverage
npm run test:coverageCoverage Results:
- β 87.63% Statements
- β 79.05% Branches
- β 76.19% Functions
- β 87.63% Lines
Full GraphQL Schema
enum Lang {
EN
FR
}
type User {
uuid: ID!
mail: String!
verified: Boolean!
member_since: String!
}
type Query {
me: User!
}
type Mutation {
# User Registration
create_user(mail: String!, pwd: String!, lang: Lang!): Boolean!
invite_user(mail: String!): ID!
# Session Management
create_session(mail: String!, pwd: String!, remember: Boolean): Boolean!
delete_session(id: String): Boolean!
refresh_session: Boolean!
# Account Verification
create_account_confirm_code(lang: Lang!): Boolean!
confirm_account(code: String!): Boolean!
# Password Management
create_pwd_reset_code(mail: String!, lang: Lang!): Boolean!
update_pwd(code: String!, new_pwd: String!): Boolean!
update_pwd_logged(current_pwd: String!, new_pwd: String!): Boolean!
# Admin Operations
admin_update_pwd(mail: String!, new_pwd: String!): Boolean!
admin_delete_users(mails: [String!]!): Boolean!
}- Password Requirements: 6-32 characters, must include letter + digit, no whitespace
- Email Validation: RFC-compliant email regex
- JWT Algorithm: ES512 (ECDSA P-521 + SHA-512)
- Password Hashing: bcrypt with configurable rounds (default: 12)
- Rate Limiting: Configurable delays on password reset and account confirmation
- Session Security: HTTP-only cookies, CORS protection, user agent tracking
- Production Keys: MUST set PUBLIC_KEY and PRIVATE_KEY env vars (dev keys will error in production)
MAIL_USED- Email already registeredMAIL_INVALID- Invalid email formatPASSWORD_INVALID- Password doesn't meet requirementsUSER_NOT_FOUND- Invalid credentials or user doesn't existILLEGAL_SESSION- Invalid session or missing user agentNO_PASSWORD- Invited user hasn't set password yetINVALID_CODE- Verification/reset code expired or invalidUNAUTHORIZED- Authentication requiredREGISTRATION_DISABLED- Registration not allowed (ALLOW_REGISTRATION=false)SPAM- Rate limit exceededMAIL_SERVICE_OFFLINE- Email notification service unavailable
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing) - Commit changes (
git commit -m 'feat: add amazing feature') - Push to branch (
git push origin feature/amazing) - Open Pull Request
MIT Β© Hydre