@lokalise/auth
A flexible, type-safe authentication library for Fastify applications built with TypeScript. This library provides a
clean abstraction for JWT-based authentication with support for multiple authentication strategies and
token verification methods.
Features
- 🔐 JWT-based Authentication - Built-in support for JWT token verification
- 🔗 Multiple Token Decoders - Support for JWKS and static key verification
- ⛓️ Authenticator Chains - Chain multiple authenticators for complex authentication flows
- 🚀 Fastify Integration - Seamless integration with Fastify request lifecycle
- 🛡️ Type Safety - Full TypeScript support with generic types
Quick Start
Basic JWT Authentication
import { FastifyInstance } from 'fastify'
import {
JwtBasedAuthenticator,
KeyBasedTokenDecoder,
createAuthenticationPreHandler,
type BaseAuthInfo
} from '@lokalise/auth'
type MyAuthInfo = BaseAuthInfo<'my-provider'> & {
userId: string
email: string
}
class MyAuthenticator extends JwtBasedAuthenticator<MyAuthInfo> {
protected internalAuthenticate(reqContext, jwtPayload, rawToken) {
if (!jwtPayload.sub || !jwtPayload.email) {
return { success: false, failure: 'INVALID_CREDENTIALS' }
}
return {
success: true,
authInfo: {
authType: 'my-provider',
rawToken,
userId: jwtPayload.sub,
email: jwtPayload.email
}
}
}
}
const tokenDecoder = new KeyBasedTokenDecoder('your-secret-key')
const authenticator = new MyAuthenticator(tokenDecoder)
const authPreHandler = createAuthenticationPreHandler(authenticator)
const fastify: FastifyInstance =
fastify.addHook('preHandler', authPreHandler)
fastify.get('/protected', async (request, reply) => {
const authInfo = request.reqContext.auth
return { message: `Hello ${authInfo?.email}!` }
})
JWKS Authentication (OAuth/OpenID Connect)
import { JwksTokenDecoder } from '@lokalise/auth'
import { createJwksClient } from 'jwks-rsa'
const jwksClient = createJwksClient({
jwksUri: 'https://your-oauth-provider.com/.well-known/jwks',
cache: true,
cacheMaxAge: 600000,
})
const tokenDecoder = new JwksTokenDecoder(jwksClient, {
algorithms: ['RS256'],
issuer: 'https://your-oauth-provider.com'
})
const authenticator = new MyAuthenticator(tokenDecoder)
const authPreHandler = createAuthenticationPreHandler(authenticator)
Multiple Authentication Strategies
import { AuthenticatorChain } from '@lokalise/auth'
const jwtAuthenticator = new MyJwtAuthenticator(jwtDecoder)
const apiKeyAuthenticator = new MyApiKeyAuthenticator()
const chain = new AuthenticatorChain([
jwtAuthenticator,
apiKeyAuthenticator
])
const authPreHandler = createAuthenticationPreHandler(chain)
API Reference
Core Types
BaseAuthInfo<AuthType>
Base interface for authentication information:
type BaseAuthInfo<AuthType extends string> = {
authType: AuthType
rawToken: string
}
AuthResult<AuthInfo>
Result of authentication attempt:
type AuthResult<AuthInfo> =
| { success: true; authInfo: AuthInfo }
| { success: false; failure: 'INVALID_CREDENTIALS' | 'EXPIRED_CREDENTIALS' }
Authenticators
Authenticator<AuthInfo>
Base interface for all authenticators:
interface Authenticator<AuthInfo extends BaseAuthInfo<string>> {
authenticate(request: FastifyRequest): Promise<AuthResult<AuthInfo>>
}
JwtBasedAuthenticator<AuthInfo>
Abstract base class for JWT-based authentication:
abstract class JwtBasedAuthenticator<AuthInfo extends BaseAuthInfo<string>> {
constructor(tokenDecoder: TokenDecoder, tokenHeader?: string)
protected abstract internalAuthenticate(
reqContext: RequestContext,
jwtPayload: object,
rawToken: string
): AuthResult<AuthInfo> | Promise<AuthResult<AuthInfo>>
}
AuthenticatorChain<AuthInfo>
Chains multiple authenticators, trying each until one succeeds:
class AuthenticatorChain<AuthInfo extends BaseAuthInfo<string>> {
constructor(authentators: Authenticator<AuthInfo>[])
}
Token Decoders
TokenDecoder
Base class for token verification:
class TokenDecoder {
constructor(decode: (token: string) => Promise<unknown> | unknown)
decode(requestContext: RequestContext, token: string): Promise<Either<TokenValidationError, object>>
}
KeyBasedTokenDecoder
Uses a static key for JWT verification:
class KeyBasedTokenDecoder extends TokenDecoder {
constructor(key: string, options?: Partial<VerifierOptions>)
}
JwksTokenDecoder
Uses JWKS endpoint for JWT verification:
class JwksTokenDecoder extends TokenDecoder {
constructor(jwksClient: JwksClient, options?: Partial<VerifierOptions>)
}
Fastify Integration
createAuthenticationPreHandler<AuthInfo>
Creates a Fastify pre-handler for authentication:
function createAuthenticationPreHandler<AuthInfo extends BaseAuthInfo<string>>(
authenticator: Authenticator<AuthInfo>
): (request: FastifyRequest) => Promise<void>
Error Handling
The library provides consistent error handling with proper HTTP status codes:
- 401 Unauthorized - For invalid or expired credentials
- Proper Error Codes -
INVALID_CREDENTIALS
or EXPIRED_CREDENTIALS
try {
await authPreHandler(request)
} catch (error) {
if (error instanceof PublicNonRecoverableError) {
console.log(error.httpStatusCode)
console.log(error.errorCode)
}
}
Request Context Extension
The library extends Fastify-extras RequestContext
with authentication information:
declare module '@lokalise/fastify-extras' {
interface RequestContext {
auth?: BaseAuthInfo<string>
}
}
You can specify custom headers for token extraction:
const authenticator = new MyAuthenticator(tokenDecoder, 'x-custom-auth')