TypeScript API / @laikacms/server/oauth2 / OAuthConfig
Interface: OAuthConfig
Properties
accessTokenExpiration?
optional accessTokenExpiration?: number;allowedRedirectUris
allowedRedirectUris: string[];Redirect URIs permitted to receive an authorization code, compared by exact string match (RFC 6749 §3.1.2.2, RFC 9700 §2.1).
Required, and deliberately so. Accepting an arbitrary redirect_uri turns one clicked link into account takeover: the attacker supplies their own PKCE challenge, the victim completes the whole login — password, TOTP, passkey — and the code is delivered to the attacker's host, where their own verifier redeems it. There is no client secret in the token path to stop that, so this list is the control.
Example
['https://cms.example.com/admin/', 'http://localhost:8080/admin/']authCodeExpiration?
optional authCodeExpiration?: number;authorizeEndpoint?
optional authorizeEndpoint?: string;Override the authorize endpoint path. Defaults to <basePath>/authorize.
basePath
basePath: string;callbacks
callbacks: OAuthCallbacks;captcha?
optional captcha?: CaptchaConfig;CAPTCHA configuration - if provided, enables CAPTCHA on login and forgot password forms
clientId
clientId: string;customLogo?
optional customLogo?: string;Custom logo HTML to replace the default logo
customStyles?
optional customStyles?: string;Custom CSS styles to append to the default styles
ignoreUnsafeReasons?
optional ignoreUnsafeReasons?: (
| "LCMS_OAUTH2_CSPRNG_MISSING"
| "LCMS_OAUTH2_WEBCRYPTO_SUBTLE_MISSING"
| "LCMS_OAUTH2_CSPRNG_DEGENERATE"
| "LCMS_OAUTH2_INSECURE_CONTEXT"
| "LCMS_OAUTH2_NODE_UNSUPPORTED"
| "LCMS_OAUTH2_IGNORE_LIST_UNKNOWN_CODE"
| "LCMS_OAUTH2_SHA256_UNAVAILABLE"
| "LCMS_OAUTH2_HMAC_UNAVAILABLE"
| "LCMS_OAUTH2_PASSKEY_ES256_UNAVAILABLE"
| "LCMS_OAUTH2_PQ_PASSKEY_SIGNATURE"
| "LCMS_OAUTH2_PQ_PASSWORD_ONLY"
| "LCMS_OAUTH2_PQ_TRANSPORT_UNVERIFIED")[];Safety-gate reasons to suppress. Each entry acknowledges that you understand the risk and accept it for this deployment; every suppressed reason is warned about through logger on every construction.
See
UNSAFE_REASONS for the full registry of codes.
logger?
optional logger?: Logger;loginRedirectUrl?
optional loginRedirectUrl?: string;URL to redirect users to after password reset (e.g., frontend CMS admin page)
passkey?
optional passkey?: PasskeyOptions;Passkey (WebAuthn) configuration - if provided, enables passkey authentication
passwordReset?
optional passwordReset?: PasswordResetConfig;Password reset configuration - if provided, enables forgot password flow on login page
refreshTokenExpiration?
optional refreshTokenExpiration?: number;threatModel?
optional threatModel?: "standard" | "post-quantum";Which adversary this deployment defends against.
'standard'— a well-resourced classical attacker (default).'post-quantum'— also a cryptographically relevant quantum computer. Enables safety gates on primitives that have no post-quantum replacement (WebAuthn's Shor-breakable ES256/RS256, password-only authentication, and transport we cannot verify from inside the process).
tokenEndpoint?
optional tokenEndpoint?: string;Override the token endpoint path. Defaults to <basePath>/token.
totp?
optional totp?: OAuthTotpConfig;TOTP 2FA configuration - if provided, enables TOTP verification
translations?
optional translations?: object;Localized messages for user-facing strings. If not provided, defaults to English messages. Import translations from '@laikacms/server/oauth2/i18n' or provide custom messages.
auth
auth: object;auth.authenticating
authenticating: string = 'Authenticating...';auth.authenticationFailed
authenticationFailed: string = 'Authentication failed';auth.dividerOr
dividerOr: string = 'or';auth.emailLabel
emailLabel: string = 'Email';auth.emailPlaceholder
emailPlaceholder: string = 'Email';auth.forgotPasswordLink
forgotPasswordLink: string = 'Forgot password?';auth.loginButton
loginButton: string = 'Login';auth.noCredentialReturned
noCredentialReturned: string = 'No credential returned';auth.pageTitle
pageTitle: string = 'Sign In - Laika CMS';auth.passkeyAuthFailed
passkeyAuthFailed: string = 'Passkey authentication failed. Please try again or use password.';auth.passkeyOptionsNotAvailable
passkeyOptionsNotAvailable: string = 'Passkey options not available';auth.passwordLabel
passwordLabel: string = 'Password';auth.passwordPlaceholder
passwordPlaceholder: string = 'Password';auth.signInButton
signInButton: string = 'Sign In';auth.signingInWithPasskey
signingInWithPasskey: string = 'Signing in with passkey...';auth.signInWithPasskey
signInWithPasskey: string = 'Sign in with Passkey';common
common: object;common.back
back: string = 'Back';common.cancel
cancel: string = 'Cancel';common.continue
continue: string = 'Continue';common.error
error: string = 'Error';common.loading
loading: string = 'Loading...';common.submit
submit: string = 'Submit';common.success
success: string = 'Success';email
email: object;email.passwordResetAction
passwordResetAction: string = 'Click the button below to reset your password:';email.passwordResetButton
passwordResetButton: string = 'Reset Password';email.passwordResetFooter
passwordResetFooter: string = 'This email was sent by {{appName}}.';email.passwordResetGreeting
passwordResetGreeting: string = 'Hi{{name}},';email.passwordResetIntro
passwordResetIntro: string = 'We received a request to reset the password for your account associated with <strong>{{email}}</strong>.';email.passwordResetLinkHint
passwordResetLinkHint: string = 'Or copy and paste this link into your browser:';email.passwordResetSubject
passwordResetSubject: string = 'Reset Your Password';email.passwordResetSupport
passwordResetSupport: string = 'If you have questions, contact';email.passwordResetTitle
passwordResetTitle: string = 'Reset Your Password';email.passwordResetWarning
passwordResetWarning: string = "⚠️ This link will expire in <strong>{{expiresIn}}</strong>. If you didn't request a password reset, you can safely ignore this email.";error
error: object;error.defaultDescription
defaultDescription: string = 'An error occurred during authentication.';error.goBackButton
goBackButton: string = 'Go Back';error.pageTitle
pageTitle: string = 'Error - {{title}}';logout
logout: object;logout.allDescription
allDescription: string = 'You have been successfully logged out of all your sessions on all devices.';logout.allPageTitle
allPageTitle: string = 'Logged Out Everywhere - Laika CMS';logout.allTitle
allTitle: string = 'Logged Out Everywhere';logout.backToLogin
backToLogin: string = 'Back to login';logout.description
description: string = 'You have been successfully logged out of your current session.';logout.pageTitle
pageTitle: string = 'Logged Out - Laika CMS';logout.title
title: string = "You've Been Logged Out";passkey
passkey: object;passkey.browserNotSupported
browserNotSupported: string = 'Your browser does not support passkeys. Please use a modern browser like Chrome, Safari, or Firefox.';passkey.createButton
createButton: string = 'Create Passkey';passkey.description
description: string = 'Passkeys are a more secure and convenient way to sign in.';passkey.loadingPrompt
loadingPrompt: string = 'Follow the prompts from your browser or device...';passkey.nameLabel
nameLabel: string = 'Passkey Name (optional)';passkey.namePlaceholder
namePlaceholder: string = 'e.g., MacBook Pro, iPhone';passkey.pageTitle
pageTitle: string = 'Set Up Passkey - Laika CMS';passkey.registrationFailed
registrationFailed: string = 'Registration failed';passkey.setupFailed
setupFailed: string = 'Failed to set up passkey. Please try again.';passkey.skipLink
skipLink: string = 'Skip for now';passkey.successDescription
successDescription: string = 'You can now use this passkey to sign in. Redirecting...';passkey.successTitle
successTitle: string = 'Passkey Created!';passkey.title
title: string = 'Set Up a Passkey';passwordReset
passwordReset: object;passwordReset.backToLogin
backToLogin: string = 'Back to login';passwordReset.checkEmailDescription
checkEmailDescription: string = "If an account exists with that email address, we've sent you a link to reset your password. Please check your inbox and spam folder.";passwordReset.checkEmailPageTitle
checkEmailPageTitle: string = 'Check Your Email - Laika CMS';passwordReset.checkEmailTitle
checkEmailTitle: string = 'Check Your Email';passwordReset.confirmPasswordLabel
confirmPasswordLabel: string = 'Confirm Password';passwordReset.confirmPasswordPlaceholder
confirmPasswordPlaceholder: string = 'Confirm new password';passwordReset.enterEmailError
enterEmailError: string = 'Please enter your email address';passwordReset.forgotDescription
forgotDescription: string = "Enter your email address and we'll send you a link to reset your password.";passwordReset.forgotPageTitle
forgotPageTitle: string = 'Forgot Password - Laika CMS';passwordReset.forgotTitle
forgotTitle: string = 'Forgot Password';passwordReset.linkExpiredError
linkExpiredError: string = 'This reset link has expired or is invalid. Please request a new one.';passwordReset.newPasswordLabel
newPasswordLabel: string = 'New Password';passwordReset.newPasswordPlaceholder
newPasswordPlaceholder: string = 'Enter new password';passwordReset.passwordHint
passwordHint: string = 'Must be at least 8 characters';passwordReset.passwordMinLengthError
passwordMinLengthError: string = 'Password must be at least 8 characters';passwordReset.passwordMismatchError
passwordMismatchError: string = 'Passwords do not match';passwordReset.resetButton
resetButton: string = 'Reset Password';passwordReset.resetDescription
resetDescription: string = 'Enter your new password below.';passwordReset.resetPageTitle
resetPageTitle: string = 'Reset Password - Laika CMS';passwordReset.resetTitle
resetTitle: string = 'Reset Password';passwordReset.sendResetLink
sendResetLink: string = 'Send Reset Link';passwordReset.signInLink
signInLink: string = 'Sign in';passwordReset.successDescription
successDescription: string = 'Your password has been reset successfully. You can now sign in with your new password.';passwordReset.successPageTitle
successPageTitle: string = 'Password Reset Successful - Laika CMS';passwordReset.successTitle
successTitle: string = 'Password Reset Successful';totp
totp: object;totp.description
description: string = 'Enter the 6-digit code from your authenticator app';totp.enterVerificationCode
enterVerificationCode: string = 'Enter verification code';totp.inputLabel
inputLabel: string = 'One-time code';totp.inputPlaceholder
inputPlaceholder: string = '000000';totp.manualKeyCopied
manualKeyCopied: string = 'Copied!';totp.manualKeyLabel
manualKeyLabel: string = 'Or enter this key manually:';totp.pageTitle
pageTitle: string = 'Two-Factor Authentication - Laika CMS';totp.qrCodeAlt
qrCodeAlt: string = 'QR Code for {{issuer}}';totp.setupPageTitle
setupPageTitle: string = 'Set Up Two-Factor Authentication - Laika CMS';totp.setupStep1
setupStep1: string = 'Download an authenticator app like Google Authenticator, Authy, or 1Password';totp.setupStep2
setupStep2: string = 'Scan the QR code below or enter the key manually';totp.setupStep3
setupStep3: string = 'Enter the 6-digit code to verify';totp.setupTitle
setupTitle: string = 'Set Up Two-Factor Authentication';totp.title
title: string = 'Two-Factor Authentication';totp.verifyAndContinue
verifyAndContinue: string = 'Verify & Continue';totp.verifyButton
verifyButton: string = 'Verify';Example
import { nl } from '@laikacms/server/oauth2/i18n';
const config: OAuthConfig = {
translations: nl,
// ... other options
};