Skip to content

TypeScript API / @laikacms/server/oauth2 / OAuthConfig

Interface: OAuthConfig ​

Properties ​

accessTokenExpiration? ​

ts
optional accessTokenExpiration?: number;

allowedRedirectUris ​

ts
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 ​

ts
['https://cms.example.com/admin/', 'http://localhost:8080/admin/']

authCodeExpiration? ​

ts
optional authCodeExpiration?: number;

authorizeEndpoint? ​

ts
optional authorizeEndpoint?: string;

Override the authorize endpoint path. Defaults to <basePath>/authorize.


basePath ​

ts
basePath: string;

callbacks ​

ts
callbacks: OAuthCallbacks;

captcha? ​

ts
optional captcha?: CaptchaConfig;

CAPTCHA configuration - if provided, enables CAPTCHA on login and forgot password forms


clientId ​

ts
clientId: string;

ts
optional customLogo?: string;

Custom logo HTML to replace the default logo


customStyles? ​

ts
optional customStyles?: string;

Custom CSS styles to append to the default styles


ignoreUnsafeReasons? ​

ts
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? ​

ts
optional logger?: Logger;

loginRedirectUrl? ​

ts
optional loginRedirectUrl?: string;

URL to redirect users to after password reset (e.g., frontend CMS admin page)


passkey? ​

ts
optional passkey?: PasskeyOptions;

Passkey (WebAuthn) configuration - if provided, enables passkey authentication


passwordReset? ​

ts
optional passwordReset?: PasswordResetConfig;

Password reset configuration - if provided, enables forgot password flow on login page


refreshTokenExpiration? ​

ts
optional refreshTokenExpiration?: number;

threatModel? ​

ts
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? ​

ts
optional tokenEndpoint?: string;

Override the token endpoint path. Defaults to <basePath>/token.


totp? ​

ts
optional totp?: OAuthTotpConfig;

TOTP 2FA configuration - if provided, enables TOTP verification


translations? ​

ts
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 ​

ts
auth: object;
auth.authenticating ​
ts
authenticating: string = 'Authenticating...';
auth.authenticationFailed ​
ts
authenticationFailed: string = 'Authentication failed';
auth.dividerOr ​
ts
dividerOr: string = 'or';
auth.emailLabel ​
ts
emailLabel: string = 'Email';
auth.emailPlaceholder ​
ts
emailPlaceholder: string = 'Email';
ts
forgotPasswordLink: string = 'Forgot password?';
auth.loginButton ​
ts
loginButton: string = 'Login';
auth.noCredentialReturned ​
ts
noCredentialReturned: string = 'No credential returned';
auth.pageTitle ​
ts
pageTitle: string = 'Sign In - Laika CMS';
auth.passkeyAuthFailed ​
ts
passkeyAuthFailed: string = 'Passkey authentication failed. Please try again or use password.';
auth.passkeyOptionsNotAvailable ​
ts
passkeyOptionsNotAvailable: string = 'Passkey options not available';
auth.passwordLabel ​
ts
passwordLabel: string = 'Password';
auth.passwordPlaceholder ​
ts
passwordPlaceholder: string = 'Password';
auth.signInButton ​
ts
signInButton: string = 'Sign In';
auth.signingInWithPasskey ​
ts
signingInWithPasskey: string = 'Signing in with passkey...';
auth.signInWithPasskey ​
ts
signInWithPasskey: string = 'Sign in with Passkey';

common ​

ts
common: object;
common.back ​
ts
back: string = 'Back';
common.cancel ​
ts
cancel: string = 'Cancel';
common.continue ​
ts
continue: string = 'Continue';
common.error ​
ts
error: string = 'Error';
common.loading ​
ts
loading: string = 'Loading...';
common.submit ​
ts
submit: string = 'Submit';
common.success ​
ts
success: string = 'Success';

email ​

ts
email: object;
email.passwordResetAction ​
ts
passwordResetAction: string = 'Click the button below to reset your password:';
email.passwordResetButton ​
ts
passwordResetButton: string = 'Reset Password';
email.passwordResetFooter ​
ts
passwordResetFooter: string = 'This email was sent by {{appName}}.';
email.passwordResetGreeting ​
ts
passwordResetGreeting: string = 'Hi{{name}},';
email.passwordResetIntro ​
ts
passwordResetIntro: string = 'We received a request to reset the password for your account associated with <strong>{{email}}</strong>.';
email.passwordResetLinkHint ​
ts
passwordResetLinkHint: string = 'Or copy and paste this link into your browser:';
email.passwordResetSubject ​
ts
passwordResetSubject: string = 'Reset Your Password';
email.passwordResetSupport ​
ts
passwordResetSupport: string = 'If you have questions, contact';
email.passwordResetTitle ​
ts
passwordResetTitle: string = 'Reset Your Password';
email.passwordResetWarning ​
ts
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 ​

ts
error: object;
error.defaultDescription ​
ts
defaultDescription: string = 'An error occurred during authentication.';
error.goBackButton ​
ts
goBackButton: string = 'Go Back';
error.pageTitle ​
ts
pageTitle: string = 'Error - {{title}}';

logout ​

ts
logout: object;
logout.allDescription ​
ts
allDescription: string = 'You have been successfully logged out of all your sessions on all devices.';
logout.allPageTitle ​
ts
allPageTitle: string = 'Logged Out Everywhere - Laika CMS';
logout.allTitle ​
ts
allTitle: string = 'Logged Out Everywhere';
logout.backToLogin ​
ts
backToLogin: string = 'Back to login';
logout.description ​
ts
description: string = 'You have been successfully logged out of your current session.';
logout.pageTitle ​
ts
pageTitle: string = 'Logged Out - Laika CMS';
logout.title ​
ts
title: string = "You've Been Logged Out";

passkey ​

ts
passkey: object;
passkey.browserNotSupported ​
ts
browserNotSupported: string = 'Your browser does not support passkeys. Please use a modern browser like Chrome, Safari, or Firefox.';
passkey.createButton ​
ts
createButton: string = 'Create Passkey';
passkey.description ​
ts
description: string = 'Passkeys are a more secure and convenient way to sign in.';
passkey.loadingPrompt ​
ts
loadingPrompt: string = 'Follow the prompts from your browser or device...';
passkey.nameLabel ​
ts
nameLabel: string = 'Passkey Name (optional)';
passkey.namePlaceholder ​
ts
namePlaceholder: string = 'e.g., MacBook Pro, iPhone';
passkey.pageTitle ​
ts
pageTitle: string = 'Set Up Passkey - Laika CMS';
passkey.registrationFailed ​
ts
registrationFailed: string = 'Registration failed';
passkey.setupFailed ​
ts
setupFailed: string = 'Failed to set up passkey. Please try again.';
ts
skipLink: string = 'Skip for now';
passkey.successDescription ​
ts
successDescription: string = 'You can now use this passkey to sign in. Redirecting...';
passkey.successTitle ​
ts
successTitle: string = 'Passkey Created!';
passkey.title ​
ts
title: string = 'Set Up a Passkey';

passwordReset ​

ts
passwordReset: object;
passwordReset.backToLogin ​
ts
backToLogin: string = 'Back to login';
passwordReset.checkEmailDescription ​
ts
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 ​
ts
checkEmailPageTitle: string = 'Check Your Email - Laika CMS';
passwordReset.checkEmailTitle ​
ts
checkEmailTitle: string = 'Check Your Email';
passwordReset.confirmPasswordLabel ​
ts
confirmPasswordLabel: string = 'Confirm Password';
passwordReset.confirmPasswordPlaceholder ​
ts
confirmPasswordPlaceholder: string = 'Confirm new password';
passwordReset.enterEmailError ​
ts
enterEmailError: string = 'Please enter your email address';
passwordReset.forgotDescription ​
ts
forgotDescription: string = "Enter your email address and we'll send you a link to reset your password.";
passwordReset.forgotPageTitle ​
ts
forgotPageTitle: string = 'Forgot Password - Laika CMS';
passwordReset.forgotTitle ​
ts
forgotTitle: string = 'Forgot Password';
passwordReset.linkExpiredError ​
ts
linkExpiredError: string = 'This reset link has expired or is invalid. Please request a new one.';
passwordReset.newPasswordLabel ​
ts
newPasswordLabel: string = 'New Password';
passwordReset.newPasswordPlaceholder ​
ts
newPasswordPlaceholder: string = 'Enter new password';
passwordReset.passwordHint ​
ts
passwordHint: string = 'Must be at least 8 characters';
passwordReset.passwordMinLengthError ​
ts
passwordMinLengthError: string = 'Password must be at least 8 characters';
passwordReset.passwordMismatchError ​
ts
passwordMismatchError: string = 'Passwords do not match';
passwordReset.resetButton ​
ts
resetButton: string = 'Reset Password';
passwordReset.resetDescription ​
ts
resetDescription: string = 'Enter your new password below.';
passwordReset.resetPageTitle ​
ts
resetPageTitle: string = 'Reset Password - Laika CMS';
passwordReset.resetTitle ​
ts
resetTitle: string = 'Reset Password';
ts
sendResetLink: string = 'Send Reset Link';
ts
signInLink: string = 'Sign in';
passwordReset.successDescription ​
ts
successDescription: string = 'Your password has been reset successfully. You can now sign in with your new password.';
passwordReset.successPageTitle ​
ts
successPageTitle: string = 'Password Reset Successful - Laika CMS';
passwordReset.successTitle ​
ts
successTitle: string = 'Password Reset Successful';

totp ​

ts
totp: object;
totp.description ​
ts
description: string = 'Enter the 6-digit code from your authenticator app';
totp.enterVerificationCode ​
ts
enterVerificationCode: string = 'Enter verification code';
totp.inputLabel ​
ts
inputLabel: string = 'One-time code';
totp.inputPlaceholder ​
ts
inputPlaceholder: string = '000000';
totp.manualKeyCopied ​
ts
manualKeyCopied: string = 'Copied!';
totp.manualKeyLabel ​
ts
manualKeyLabel: string = 'Or enter this key manually:';
totp.pageTitle ​
ts
pageTitle: string = 'Two-Factor Authentication - Laika CMS';
totp.qrCodeAlt ​
ts
qrCodeAlt: string = 'QR Code for {{issuer}}';
totp.setupPageTitle ​
ts
setupPageTitle: string = 'Set Up Two-Factor Authentication - Laika CMS';
totp.setupStep1 ​
ts
setupStep1: string = 'Download an authenticator app like Google Authenticator, Authy, or 1Password';
totp.setupStep2 ​
ts
setupStep2: string = 'Scan the QR code below or enter the key manually';
totp.setupStep3 ​
ts
setupStep3: string = 'Enter the 6-digit code to verify';
totp.setupTitle ​
ts
setupTitle: string = 'Set Up Two-Factor Authentication';
totp.title ​
ts
title: string = 'Two-Factor Authentication';
totp.verifyAndContinue ​
ts
verifyAndContinue: string = 'Verify & Continue';
totp.verifyButton ​
ts
verifyButton: string = 'Verify';

Example ​

typescript
import { nl } from '@laikacms/server/oauth2/i18n';
const config: OAuthConfig = {
  translations: nl,
  // ... other options
};

Released under the MIT License.