JavaScript Login

@lemonadejs/login · ✓ 36 contract checks · framework-agnostic · zero dependencies

<Login /> — multi-screen authentication block, ported from the v5 plugin with behavioral parity. Seven screens on one endpoint:

login username + sha512(password) + remember register profile { company, name, login, username, terms, phone } forgot { username, recovery: 1 } → code screen on success code { h: sha512(code) } (6 digits) → reset screen on success reset { h, password: sha512(password) } (repeat must match) bind server action ‘bindSocialAccount’: previous payload + password terms server action ‘acceptTermsAndConditions’: payload + terms

Protocol (v5): POST to url (default: current pathname), credentials included, device token appended as ?token=. Response { success: 1 } proceeds — server may answer action: ‘resetPassword’ (+hash) to force the reset screen, or data: to demand a captcha (the captcha input appears and every following request carries captcha). Without onsuccess, the block redirects to result.url || pathname (after 3s when there is a message to read — v5 timing).

v5 → v6 mapping: google + google-client-id merged into google (the client id IS the switch; same for microsoft); require-company/phone/ username/terms → company/phone/username/terms; setTerms() → termstext; jSuites.notification → inline lm-login-message/lm-login-alert; onupdate (broken in v5 — referenced an undefined variable) → onchangescreen(screen). Email persists in localStorage(‘username’), ?create opens register, ?h=<hash> opens reset — all v5 behaviors.

Example

live
import { html } from 'lemonadejs';
import Login from '@lemonadejs/login';

// A fake backend so every screen can be tried without a server:
// any email works and the password is "demo" (the block posts its sha512).
const DEMO = '26c669cd0814ac40e5328752b21c4aa6450d16295e4eec30356a06a911c23983'
    + 'aaebe12d5da38eeebfc1b213be650498df8419194d5a26c7e0a50af156853c79';
const realFetch = window.fetch.bind(window);
window.fetch = async (url, init) => {
    if (!String(url).startsWith('/auth')) return realFetch(url, init);
    const data = JSON.parse(init.body || '{}');
    const ok = data.recovery || data.h || data.name !== undefined || data.password === DEMO;
    const body = ok ? { success: 1, message: 'Welcome back!' } : { success: 0, message: 'Wrong password (try "demo")' };
    return new Response(JSON.stringify(body), { headers: { 'Content-Type': 'application/json' } });
};

const App = (props, { state }) => {
    const status = state('Any email, password "demo"');

    return html`<div style="max-width:380px">
        <${Login} url="/auth" remember profile
            onsuccess="${(r) => (status.value = r.message)}"
            onerror="${(r) => (status.value = r.message)}"
            onchangescreen="${(s) => (status.value = 'Screen: ' + s)}" />
        <p>${status}</p>
    </div>`;
};

Installation

npm install @lemonadejs/login
import Login from '@lemonadejs/login';
import '@lemonadejs/login/style.css';

Three deployment forms, one component:

html`<${Login} />`                       // by value (no registration)
setComponents({ Login });               // then <Login /> by name anywhere
createWebComponent(Login);              // <lm-login> in plain HTML/any framework

Props

Every declared prop arrives as a live state — pass a value for a snapshot or a state for a two-way live wire. Attribute strings are coerced to the declared type.

PropTypeDefaultDescription
urlstring''endpoint (v5: url; default = current pathname)
devicestring''device token, appended as ?token= (v5)
logostring''logo image url (v5)
fullscreenbooleanfalsecover the viewport (v5)
googlestring''Google client id — truthy shows the button (v5: google + google-client-id)
facebookbooleanfalseshow the Facebook button (FB SDK carries its own app id)
microsoftstring''Microsoft client id — truthy shows the button (v5: microsoft + microsoft-client-id)
rememberbooleanfalseoffer “remember me” (v5: visibility AND initial checked)
profilebooleanfalseoffer the “create a new profile” link (v5)
companybooleanfalseregistration collects company (v5: require-company)
phonebooleanfalseregistration collects phone (v5: require-phone)
usernamebooleanfalseregistration collects username (v5: require-username)
termsbooleanfalseregistration requires terms acceptance (v5: require-terms)
termstextstring''custom terms label, trusted HTML (v5: setTerms)

Events

All event names are lowercase (the platform convention — LJS-305 warns otherwise).

  • onload — after mount (v5)
  • onsuccess — (result, data) — replaces the redirect (v5)
  • onerror — (result) — server refusals and network failures (v5)
  • onbeforesend — (data) — mutate the payload before POST (v5)
  • onbeforecreate — (profile) — before register/social create (v5)
  • onchangescreen — (screen) — replaces v5’s broken onupdate

API

import { ref } from 'lemonadejs';
const login = ref();
html`<${Login} ref="${login}" />`;
// login.current.show(...)
  • show()

Styling

All classes follow the lm-login-* convention; visual variants are data-* attributes on the root. Override freely — there is no styling engine to fight.

Contract

The machine-readable schema ships with the package:

import contract from '@lemonadejs/login/contract.json';

verify.json carries the conformance proof produced by verify(Login).

Looking for the v5 plugin? See the archived v5 documentation.