Built into the language

Authentication That Just Works

No Passport.js. No Auth0. No NextAuth. FLIN has 11 authentication methods as native functions — session management, OAuth with 8 providers, OTP, 2FA, JWT, and bcrypt. All compiled in. All zero-config.

11 Authentication Methods

Every method is a native function. No SDKs, no external services, no API keys for basic auth. Just call the function and authenticate.

Email / Password

Classic login with bcrypt_hash() and bcrypt_verify(). Deliberately slow hashing, auto-salted, resistant to brute-force and rainbow table attacks.

Email OTP

Passwordless login via one-time codes. otp_generate(6) creates the code, send_email() delivers it. Verify and auto-create accounts.

WhatsApp OTP

whatsapp_send_otp(phone) sends a verification code via WhatsApp. Perfect for African and South Asian markets where WhatsApp is the primary channel.

TOTP / 2FA

Two-factor authentication with totp_secret() and totp_verify(). Compatible with Google Authenticator, Authy, and any TOTP app.

JWT Tokens

jwt_encode() and jwt_decode() with configurable expiry and custom claims. Stateless authentication for APIs and microservices.

Session Management

Built-in session.* system with encrypted cookies. Set session.user on login, check in middleware, clear on logout. 1-year expiry, SameSite=Lax.

8 OAuth Providers. Two Functions Each.

Every OAuth provider is a pair of native functions: one generates the login URL, one handles the callback. No SDK installation, no middleware chain.

Google

OAuth 2.0 with PKCE

auth_google_login / callback

GitHub

OAuth 2.0 with PKCE

auth_github_login / callback

Discord

OAuth 2.0 standard

auth_discord_login / callback

Apple

Sign-In with Apple (ES256 JWT)

auth_apple_login / callback

LinkedIn

OpenID Connect

auth_linkedin_login / callback

Telegram

HMAC-SHA256 widget verification

verify_telegram_login

Clerk

JWT verification + Backend API

verify_clerk_token

Firebase

JWT claim validation

verify_firebase_token

See It in Action

From login form to protected dashboard in three files. No boilerplate, no configuration, no external services.

app/login.flin
email = ""
password = ""
errorKey = ""

<h1>Login</h1>

{if errorKey != ""}
    <p class="error">{t(errorKey)}</p>
{/if}

<input bind={email} placeholder="Email" />
<input type="password" bind={password} placeholder="Password" />

<button click={
    if email == "" { errorKey = "error.email_required" }
    else {
        session.loginEmail = email
        session.loginPass = password
        location.href = "/auth/process-login"
    }
}>Log In</button>

<!-- OAuth providers — use <a> tags, not click handlers -->
<a href={auth_google_login().url}>Continue with Google</a>
<a href={auth_github_login().url}>Continue with GitHub</a>
app/auth/process-login.flin
loginEmail = session.loginEmail || ""
loginPass = session.loginPass || ""
session.loginPass = none       // Clear password IMMEDIATELY
loginOk = false

fn processLogin() {
    if loginEmail != "" && loginPass != "" {
        found = User.where(email == loginEmail).first
        if found != none {
            valid = bcrypt_verify(loginPass, found.password)
            if valid {
                session.user = found.email
                session.userName = found.name
                session.userId = to_text(found.id)
                loginOk = true
            }
        }
    }
}
processLogin()
session.loginEmail = none

{if loginOk}
    <h2>Welcome!</h2>
    <script>setTimeout(() => window.location.href = "/dashboard", 1000)</script>
{else}
    <p>Invalid credentials.</p>
    <a href="/login">Try Again</a>
{/if}
app/_middleware.flin
middleware {
    matcher: ["/dashboard/**", "/settings"]
    exclude: ["/", "/login", "/register", "/auth/**"]

    if session.user == none {
        redirect("/login")
    }
    next()
}

Passwordless Authentication

Email OTP and WhatsApp OTP — send a code, verify it, log in. No passwords to remember, no credentials to store.

Email OTP

Generate a 6-digit code with otp_generate(6), send it via send_email(), verify on the next page. Auto-create accounts for new users.

WhatsApp OTP

whatsapp_send_otp(phone) delivers a code via WhatsApp Business Cloud API. Verify with a simple string comparison. New users complete a profile step.

Security Built In

Codes expire after 10 minutes. Session variables are cleared immediately after reading. Same error messages for "user not found" and "wrong code" to prevent enumeration.

WhatsApp OTP Flow
// Step 1: Send OTP via WhatsApp
result = whatsapp_send_otp(phone)
if result.success {
    session.waOtpCode = result.code
    session.waOtpPhone = phone
}

// Step 2: User enters the code, verify it
if otpInput == session.waOtpCode {
    found = User.where(phone == otpPhone).first
    if found != none {
        session.user = found.email    // Existing user — log in
    } else {
        // New user — redirect to profile completion
    }
}

Registration with File Upload

FLIN's route POST blocks handle multipart forms natively. Accept avatars, validate files, and create accounts — all in one route.

app/auth/process-register.flin
route POST {
    validate {
        firstName: text @required @minLength(1)
        email: text @required @email
        password: text @required @minLength(8)
        avatar: file @max_size("5MB")
    }

    existing = User.where(email == body.email).first
    if existing != none {
        redirect("/register?error=email_taken")
    }

    avatarPath = ""
    if body.avatar != none {
        avatarPath = save_file(body.avatar, ".flindb/avatars/")
    }

    newUser = User {
        email: body.email,
        password: bcrypt_hash(body.password),
        name: body.firstName,
        avatar: avatarPath,
        provider: "Email"
    }
    save newUser

    session.user = newUser.email
    session.userName = newUser.name
    session.userId = to_text(newUser.id)
    redirect("/dashboard")
}

Password Reset &amp; Account Recovery

Send a reset code via email, verify it, update the password. The full flow is two files and zero third-party services.

Send Reset Code

Generate a 6-digit code, email it to the user. Don't reveal whether the email exists — always show "code sent" to prevent enumeration.

Verify &amp; Reset

Compare codes, find the user, hash the new password with bcrypt_hash(), save. Clear all session variables immediately.

Security Patterns

Codes expire in 10 minutes. Session variables cleared on read. Generic error messages prevent email enumeration. Rate limiting on send endpoints.

The User Entity

A complete user model that supports all 11 auth methods. OAuth users have no password. WhatsApp users authenticate by phone. Every field has a purpose.

entities/User.flin
enum AuthProvider {
    Email, Google, GitHub, Discord, Apple,
    LinkedIn, Telegram, Clerk, Firebase, WhatsApp
}

enum UserRole { User, Admin, Moderator }

entity User {
    email: text @required @email
    password: text = ""               // Empty for OAuth users
    name: text = ""
    phone: text = ""
    provider: AuthProvider = "Email"
    providerId: text = ""           // OAuth provider's user ID
    avatar: text = ""
    emailVerified: bool = false
    role: UserRole = "User"

    @unique(email, role)
    @index(email)
    @index(provider)
}
// Auto: id, created_at, updated_at, deleted_at, version

Auth Functions Reference

Every function is native — compiled into the binary. No imports, no packages, no external dependencies.

Passwords

bcrypt_hash(password)
bcrypt_verify(plain, hash)

JWT

jwt_encode(payload, secret, ttl)
jwt_decode(token, secret)

TOTP / 2FA

totp_secret()
totp_verify(secret, code)

OTP

otp_generate(length)
whatsapp_send_otp(phone)

Email

send_email(to, subject, body)

Files

save_file(file, directory)

Configuration

Each provider needs only 2-4 environment variables. Email/password auth needs zero external configuration.

.env
# Base URL (required for OAuth callbacks)
BASE_URL=https://myapp.com

# Google OAuth
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your-secret

# GitHub OAuth
GITHUB_CLIENT_ID=your-client-id
GITHUB_CLIENT_SECRET=your-client-secret

# SMTP (for Email OTP and Password Reset)
SMTP_HOST=smtp.postmarkapp.com
SMTP_PORT=587
SMTP_USER=your-token
SMTP_PASSWORD=your-token
[email protected]

# WhatsApp Business Cloud API
WHATSAPP_ACCESS_TOKEN=your-access-token
WHATSAPP_PHONE_NUMBER_ID=your-phone-id
WHATSAPP_OTP_TEMPLATE_NAME=your-template

Auth in Minutes, Not Days

No Passport.js, no Auth0, no NextAuth, no Firebase Auth SDK. Just native FLIN functions. Login form to protected dashboard in three files.

curl -fsSL https://flin.sh | bash