=== SignInForge ===
Contributors: exodialabs
Tags: login, custom login, registration, password reset, login page
Requires at least: 6.9
Tested up to: 7.1
Requires PHP: 8.1
Stable tag: 0.1.1
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Design your login, registration and password reset pages with the block editor or Elementor, and keep WordPress authentication intact.

== Description ==

SignInForge is a login experience builder for WordPress. Design the pages your users see when they log in, register or reset their password with the tools you already know, the block editor or Elementor, and keep everything WordPress does behind them: the same address, the same authentication functions, the same hooks.

Your login screens become part of your brand instead of a WordPress page with a logo swapped in. And because SignInForge only changes how the screens look, the plugins that secure or extend your logins (two-factor authentication, captchas, membership and shop plugins) keep working.

= How it works =

1. Create an **experience**, a design for your login screens, from one of the bundled templates or from a blank page.
2. Place the **Authentication Form** block (or the SignInForge Auth Form widget in Elementor) where the form should appear, around your logo, text, images and columns.
3. Publish it and choose it for the login screens in SignInForge > Settings.

SignInForge serves it at the usual `wp-login.php` address. The form adapts to the screen being shown: log in, lost password, password reset or registration. You can use one design for every screen or a different one for each.

= Features =

* **Block editor designs**: build the page with core blocks and the Authentication Form block, with its own settings (which form to show, "Remember Me", navigation links).
* **Elementor designs**: when Elementor is active, edit an experience with Elementor and style the SignInForge Auth Form widget from its Style tab.
* **Six templates**: Classic, Clean, Corporate, Dark, Minimal and Split Screen, ready to customize. Export your own designs as template files (one experience or several at once, block editor and Elementor designs) and import them on another site.
* **Every login screen**: log in, lost password, password reset and registration (when registration is open), with the WordPress messages and notices.
* **Design every screen**: preview the lost password, password reset and registration forms and each message (logged out, reset link sent, expired link…) in the block editor, in Elementor, in the template previews and from SignInForge > Settings.
* **Redirects**: send users to a page of your choice after logging in and after logging out.
* **Rate limiting**, on by default: failed logins are limited per IP address and account, on every login form of the site and on XML-RPC.
* **Cloudflare Turnstile**, off by default: a privacy-friendly challenge on the login, lost password and registration forms.
* **Generic login errors**, on by default: the forms never tell whether the username or the password was wrong.
* **Always a way in**: if a design cannot be displayed, the native WordPress screen is shown instead, and a secret safe mode URL shows the native screens on demand.
* **Accessible forms**: every field has a label, errors are announced and linked to the fields.
* **Lightweight**: no front-end JavaScript of its own (only Cloudflare's script when Turnstile is on), and the form stylesheet only loads where a form is shown.
* **Guided tour**: on first use, a short interactive tour shows the experience list, the templates, the settings and the editor tools. Skip it with Esc or "Skip tour", and start it again any time with the "Guided tour" button (or from the Options menu of the block editor).
* **For developers**: documented actions and filters for the redirects, the forms, the templates, the client IP address and custom builders.

= Built on WordPress authentication =

SignInForge does not reimplement logins. Its forms call the WordPress functions (`wp_signon()`, `retrieve_password()`, `reset_password()`, `register_new_user()`) and use the same field names as `wp-login.php`. They fire the core form hooks (`login_form`, `lostpassword_form`, `resetpass_form`, `register_form`, `login_enqueue_scripts`), the message and redirect filters (`wp_login_errors`, `login_errors`, `login_redirect`, `lostpassword_redirect`, `registration_redirect`, `logout_redirect`) and, through the core functions, the authentication hooks (`authenticate`, `wp_login`, `wp_login_failed`, `lostpassword_post`, `validate_password_reset`, `registration_errors`...). So most code that adds fields to the login forms or hooks into authentication works on SignInForge screens too.

The screen hooks tied to the markup of `wp-login.php` are not fired, because SignInForge replaces that markup: `login_head`, `login_footer`, `login_message`, `login_form_{action}` and `lost_password`. See "Hooks for developers" below for the SignInForge hooks.

Rate limiting details: after 5 failed logins in 10 minutes for the same IP address and account, or 20 for the same IP address, logging in is refused for 15 minutes, even with the right password. All three values can be changed. It covers the login forms (wp-login.php, SignInForge forms and the forms of other plugins that use the WordPress login functions) and XML-RPC; REST API requests authenticated with application passwords are not counted.

= Hooks for developers =

Actions: `signinforge/loaded`, `signinforge/experience/before_render`, `signinforge/experience/after_render`, `signinforge/form/before`, `signinforge/form/after`.

Filters: `signinforge/builders` (custom page builder adapters), `signinforge/preview/use_shell`, `signinforge/redirect/after_login`, `signinforge/redirect/after_logout`, `signinforge/templates` (add templates), `signinforge/security/client_ip` (visitor IP behind a trusted proxy).

To add a field that must be submitted with a form, use the core form hooks listed above (`login_form`...), which fire inside the `<form>` element.

= Privacy =

SignInForge sends no data to its authors and contains no tracking. Rate limiting stores keyed hashes of the IP address and of the login of failed attempts, never the IP address or login themselves, and deletes them daily once expired. Cloudflare Turnstile is only used after you enable it (see External services). SignInForge adds a suggested text to the privacy policy guide in Settings > Privacy, following your settings.

== External services ==

SignInForge can use Cloudflare Turnstile, a service provided by Cloudflare, Inc., to tell humans from bots on the login, lost password and registration forms. It is off by default and only used after you enable it and enter your own site key and secret key in SignInForge > Settings.

When it is enabled:

* Browsers that display one of those forms (on wp-login.php or through a SignInForge form) load the Turnstile script from `https://challenges.cloudflare.com/turnstile/v0/api.js`. The script runs in the visitor's browser and exchanges data with Cloudflare to issue a challenge token.
* When such a form is submitted, your site sends the token, your secret key and the visitor's IP address to `https://challenges.cloudflare.com/turnstile/v0/siteverify` to check the token. Nothing is sent for XML-RPC, REST API or application password logins, or while safe mode is on.

Cloudflare website terms: https://www.cloudflare.com/website-terms/
Cloudflare privacy policy: https://www.cloudflare.com/privacypolicy/

== Installation ==

1. In your WordPress admin, go to Plugins > Add New Plugin, search for "SignInForge", then click Install Now and Activate. You can also upload the `signinforge` folder to `/wp-content/plugins/` and activate it from the Plugins screen.
2. Go to SignInForge > Templates, preview the designs on your site, then click "Use this template" under the one you like (it opens as a draft in the block editor), or start from a blank design (in the block editor, or in Elementor when it is active).
3. Customize it, keeping the Authentication Form block, then publish it.
4. Go to SignInForge > Settings, choose the experience for the login screen, and save. Open `wp-login.php` in a private window to see it.

Nothing changes on your site until you complete step 4.

== Frequently Asked Questions ==

= Does this plugin change my login page on activation? =

No. The native WordPress screens stay in place until you publish an experience and choose it for the login screens.

= How do I get started? =

Go to SignInForge > Templates, create an experience from a template, adjust the logo, colors and text in the block editor, publish it, then select it for the login screen in SignInForge > Settings. The lost password, password reset and registration screens use the same experience unless you choose another one for them. You can also click "Use on login screens" in the SignInForge > Experiences list, or tick the screens in the "Screens using this design" panel of the block editor.

= How do I design the lost password, password reset and registration screens? =

You do not need a design per screen: one experience covers them all, as its Authentication Form block set to "Automatic" (the default) shows the form of the screen being displayed. While you design, choose the screen in the toolbar of the form block ("Screen: Log in") or in the "Screen preview" panel of the block editor, or in the "Preview screen" control of the Elementor widget: the form then shows the lost password, password reset or registration form, or a message such as "Check your email" or an expired reset link, so you can style it. "Preview on this screen" opens the whole page. The template previews of SignInForge > Templates offer the same choice, and SignInForge > Settings links each screen to the preview of its design. Your visitors always see the form of the screen they open.

= How do templates work? Can I share a design between sites? =

A template creates a draft experience that you are free to change; the template itself never changes. To copy a design to another site, export it: "Export" under an experience in the SignInForge > Experiences list, the "Export" bulk action for several experiences (one file), the "Template" panel of the block editor (where you can also name and describe the template), or "Export as template" in the page settings of the Elementor editor. Then import the file in SignInForge > Templates on the other site. Imported designs are sanitized and always created as drafts, and designs containing shortcodes or embeds are refused, because they would run on the login page for every visitor. Images are not copied: the template refers to them by their URL on the original site.

= Can I reuse an Elementor design on another site? =

Yes. An exported Elementor design imports as an Elementor draft when Elementor is active on the other site, and as its block editor version otherwise (you are told so). For security, Elementor templates may only use layout elements, the basic core widgets (heading, text, image, button, icon, divider, spacer…) and the SignInForge form: the HTML and Shortcode widgets, embeds, WordPress widgets, dynamic tags, custom CSS and widgets of other plugins are refused or removed, because they could run code or load remote content on the login page.

= Can my login page have a light and a dark version? =

Yes. Every bundled template and every new design has a palette: a few named colors (background, surface, text, accent…) with a light and a dark value each. Visitors see the version that matches their system setting, unless you choose "Always light" or "Always dark" in the "Colors" panel of the block editor, where you also edit the palette and switch the editor between both versions. The template previews have a Light / Dark toggle. Only the colors picked from the palette switch: a color typed by hand stays the same in both versions. A design without palette (an imported template without one, for example) keeps fixed colors until you add a palette to it.

= Do Elementor designs get a dark version too? =

Their page background, text and the SignInForge form widget follow the palette of the experience. Elementor sections and widgets keep the colors set in Elementor, in both versions: leave their background and text colors empty to let the palette show through, or force one version in the "Colors" section of the page settings. The "Preview colors" setting of the form widget shows a version in the Elementor editor.

= What happens if my design is broken? =

SignInForge shows the native WordPress screen instead of a design that cannot be displayed: a design without the Authentication Form block, a design that is not published, or a rendering error. Your visitors can always log in.

= I cannot log in because of my design. What can I do? =

Open the safe mode URL shown in SignInForge > Settings, or add `define( 'SIGNINFORGE_SAFE_MODE', true );` to `wp-config.php`. Both show the native WordPress login screen. The safe mode URL only works for the request that carries its secret key; you can regenerate it in the settings.

= Can I design an experience with Elementor? =

Yes, when Elementor is active. Open SignInForge > Experiences, create or edit an experience and click "Edit with Elementor". Add the "SignInForge Auth Form" widget (SignInForge category) where the form must appear, style it from the widget's Style tab, then publish and choose the experience in SignInForge > Settings. Without the widget, the native screen is shown.

= Does it work with caching plugins and CDNs? =

Yes. Caching plugins and CDNs normally never cache `wp-login.php`, where SignInForge screens are served. When you place the Authentication Form block on another page, SignInForge tells page caching plugins not to cache it (`DONOTCACHEPAGE`) and sends no-cache headers, because the form carries a security token. If your CDN caches pages whatever the headers say, exclude the pages that contain the form.

= Does it work with two-factor authentication and other login plugins? =

Most plugins that hook the login form or authentication (for example two-factor authentication, captcha, membership or WooCommerce plugins) keep working, because SignInForge uses the WordPress login functions and fires the core form and authentication hooks. Plugins that rely on the screen hooks tied to the `wp-login.php` markup (`login_head`, `login_footer`, `login_message`, `login_form_{action}`, `lost_password`) will not see those hooks fire on SignInForge screens. See "Built on WordPress authentication" in the description.

= How does rate limiting identify visitors? Can I lock myself out? =

By the IP address the web server sees (`REMOTE_ADDR`), combined with the account being logged into (its username and email address count as one) or, for a login that matches no account, the text that was typed. Logins and IP addresses are stored as keyed hashes, never in clear text. A successful login clears the failures of that IP address and login. If you are locked out, wait for the lockout to end (15 minutes by default) or log in from another network; safe mode does not lift rate limiting, which is a security feature. If you already use another login limiter, you can turn this one off in SignInForge > Settings > Security.

= My site is behind a proxy, a load balancer or Cloudflare. What should I do? =

Behind a reverse proxy, every visitor may reach WordPress with the proxy's IP address, so they would share one rate limit. SignInForge never trusts forwarding headers such as `X-Forwarded-For` on its own, because anyone can forge them. If you know which header your trusted proxy sets, return the visitor IP with the `signinforge/security/client_ip` filter, for example in a must-use plugin:

`add_filter( 'signinforge/security/client_ip', function ( $ip ) { return isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) : $ip; } );`

Only do this when requests can only reach your server through that proxy. If the filter returns something that is not an IP address, SignInForge uses `REMOTE_ADDR` again.

While rate limiting is on and nothing uses this filter, the SignInForge admin screens show an error notice when your own requests carry Cloudflare's `CF-Connecting-IP` header, or come from a private or local IP address with a forwarding header, which usually means a proxy stands in front of the site.

= Turnstile blocks my login. What can I do? =

If Cloudflare cannot be reached or reports an error on its side (including a wrong secret key), the check is skipped so that nobody is locked out, and the event is logged when `WP_DEBUG` is on. If the widget itself fails (for example with a wrong site key), open the safe mode URL shown in SignInForge > Settings or define `SIGNINFORGE_SAFE_MODE`: safe mode turns Turnstile off, but not rate limiting.

= Why does the lost password form say an email was sent when the account does not exist? =

With generic login errors on (the default), the lost password form gives the same answer whether or not the account exists, so that nobody can use it to find out who has an account. If emails do not arrive, check your site's email delivery; the failures are logged when `WP_DEBUG` is on. You can turn generic errors off in SignInForge > Settings > Security.

= Which PHP and WordPress versions are supported? =

SignInForge requires PHP 8.1 or later and WordPress 6.9 or later.

= Does it support multisite? =

Not yet: on multisite, registration notices are shown instead of the registration form, and deleting the plugin only cleans the site it is deleted from. Full multisite support is planned for a later release.

= Is my data deleted when I delete the plugin? =

Only if you enable "Delete all SignInForge data" in SignInForge > Settings. Otherwise your experiences and settings are kept. The failed login counters used by rate limiting and the guided tour progress of each user are always deleted.

= Is SignInForge available in my language? =

SignInForge is translated on [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/signinforge/); language packs install automatically with your site language, and you can help translate it there. The plugin includes no translation files of its own. The bundled templates are translated too: a new experience created from a template uses the texts of the language of your administration screens.

= Where is the source code of the block editor scripts? =

The compiled scripts in `build/` are generated from the TypeScript sources in `assets/blocks/`, `assets/editor/` and `assets/onboarding/` with `@wordpress/scripts`. Both are included in the plugin: run `npm install` then `npm run build` in the plugin directory to rebuild them.

= Which third-party libraries does SignInForge include? =

The guided tour uses [Driver.js](https://driverjs.com/) 1.x by Kamran Ahmed, released under the MIT license (GPL-compatible). It is bundled in `build/onboarding/`, with its copyright and license notice in `build/onboarding/driver-js-license.txt`; nothing is loaded from another site.

= How do I see the guided tour again? =

Click "Guided tour" at the top of the SignInForge > Experiences, Templates or Settings page, or choose "Guided tour" in the Options menu of the block editor. Quitting it or finishing it is remembered for your account, so it does not start on its own again.

== Screenshots ==

1. A login screen designed with the Split Screen template.
2. The same design in dark mode, which follows the visitor's system setting, with the generic login error message.
3. The block editor: the "Screen" menu of the form block previews each login screen in its light or dark colors.
4. SignInForge > Templates: live previews of the bundled designs, and a blank design for the block editor or Elementor.
5. A template preview on any login screen, on desktop or mobile, in its light or dark version.
6. SignInForge > Settings: the design used by each login screen.
7. The security settings: rate limiting, generic login errors and Cloudflare Turnstile.
8. The SignInForge Auth Form widget in Elementor, previewing the registration screen.
9. The guided tour of the Templates page.
10. The registration, lost password and login screens on mobile, in light and dark mode.

== Changelog ==

= 0.1.1 =
* Translations are now provided by translate.wordpress.org: the plugin no longer includes its own translation files and no longer loads them itself. Until the French language pack is published, French sites see the plugin in English.
* Security hardening: every request value (nonces, the client IP address, the HTTP method, the Turnstile token, uploaded template files) is sanitized where it is read, and every admin output is escaped where it is printed, with explicit allowed HTML for the markup the plugin builds itself.
* Security hardening: the data printed for the block editor panels and the guided tour escapes quotes, ampersands and angle brackets, and the template file download sanitizes its file name.

= 0.1.0 =
* New: experiences, login page designs made with the block editor, served at wp-login.php for the log in, lost password, password reset and registration screens, one per screen or one for all.
* New: Authentication Form block, with settings for the form shown, "Remember Me" and the navigation links; it also works on regular pages.
* New: Elementor support, with the SignInForge Auth Form widget and its style controls.
* New: light and dark versions of every design: a palette of colors with a light and a dark value, shown according to the visitor's system setting or forced to one version, a "Colors" panel in the block editor to edit it and switch the editor between both versions, a Light / Dark toggle in the template previews, and template files that carry the palette.
* New: six bundled templates, and JSON export and import of experiences: block editor and Elementor designs, one experience or several at once, from the experience list, the block editor and the Elementor editor.
* Security: a shortcode outside blocks (in a Classic block, or in the fallback a page builder leaves when it is deactivated) never runs on the login page: the native screen is shown instead. Shortcode blocks still work.
* New: Templates page with live previews of each template on your site (desktop and mobile), a blank design, a category filter and a drag-and-drop import. With Elementor active, a blank design can start in the block editor or in Elementor, where it opens with the SignInForge form ready to style.
* New: the block editor shows that one design covers every login screen: a "Screen" menu in the toolbar of the form block switches the screen (and the light or dark colors) shown in the editor, and the "Screen preview" and "Screens using this design" panels open the first time.
* New: preview every screen and message of the login journey (lost password, password reset, registration, logged out, reset link sent, expired link…) in the template previews, in the block editor ("Screen preview" panel), in the Elementor widget and from each screen in Settings.
* New: like WordPress, the login screen tells users to log back in after an update, and names the application they are about to authorize.
* New: the Experiences list explains what an experience is, shows on which login screens each one is used, and offers "Use on login screens" and "Stop using" actions; a "Screens using this design" panel in the block editor does the same, and a new experience starts from a ready-made login card.
* Developers: builder adapters (`signinforge/builders`) must now implement `getLabel()`, the name shown in the Builder column of the Experiences list.
* New: after-login and after-logout redirect settings.
* New: rate limiting of failed logins, on by default, per IP address and account and per IP address, with a warning on the SignInForge screens when the site seems to be behind a reverse proxy or Cloudflare that it does not know about, or when its database table cannot be created (retried every hour).
* New: optional Cloudflare Turnstile check on the login, lost password and registration forms.
* New: generic login errors, on by default.
* New: automatic fallback to the native screens, safe mode URL and `SIGNINFORGE_SAFE_MODE` constant.
* New: suggested privacy policy text.
* New: French translation included in the plugin, bundled templates included; language packs from translate.wordpress.org take precedence.
* New: settings page with a summary of the current state, one card per section, a copy button for the safe mode URL and a save button that stays in view.
* New: a guided tour for new users, built with Driver.js: it starts on the first visit to SignInForge, continues from the Experiences list to the Templates and Settings pages, and has its own short tour in the block editor. It can be skipped or quit with Esc, is remembered per user, and starts again from the "Guided tour" button or the Options menu of the block editor.
* Uninstalling always deletes the rate limiting counters and the guided tour progress of the users; the other data is deleted only when "Delete all SignInForge data" is enabled.
