diff --git a/CHANGELOG.md b/CHANGELOG.md
index a4e00999..ccb59417 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
-
- Fixed a memory corruption issue in the builtin odbc driver manager
- ODBC: fix using globally installed system drivers by their name in debian-based linux distributions.
+ - New [login](https://sql-page.com/component.sql?component=table) component.
## v0.38.0
diff --git a/examples/official-site/examples/authentication/create_session_token.sql b/examples/official-site/examples/authentication/create_session_token.sql
index 645fedc4..8ea8cd19 100644
--- a/examples/official-site/examples/authentication/create_session_token.sql
+++ b/examples/official-site/examples/authentication/create_session_token.sql
@@ -4,12 +4,12 @@ delete from user_sessions where created_at < datetime('now', '-1 day');
-- check that the
SELECT 'authentication' AS component,
'login.sql?failed' AS link, -- redirect to the login page on error
- (SELECT password_hash FROM users WHERE username = :Username) AS password_hash, -- this is a hash of the password 'admin'
- :Password AS password; -- this is the password that the user sent through our form in 'index.sql'
+ (SELECT password_hash FROM users WHERE username = :username) AS password_hash, -- this is a hash of the password 'admin'
+ :password AS password; -- this is the password that the user sent through our form in 'index.sql'
-- if we haven't been redirected, then the password is correct
-- create a new session
-insert into user_sessions (session_token, username) values (sqlpage.random_string(32), :Username)
+insert into user_sessions (session_token, username) values (sqlpage.random_string(32), :username)
returning 'cookie' as component, 'session_token' as name, session_token as value;
-- redirect to the authentication example home page
diff --git a/examples/official-site/examples/authentication/login.sql b/examples/official-site/examples/authentication/login.sql
index b634de30..b0a39f5b 100644
--- a/examples/official-site/examples/authentication/login.sql
+++ b/examples/official-site/examples/authentication/login.sql
@@ -1,10 +1,15 @@
select 'dynamic' as component, properties FROM example WHERE component = 'shell' LIMIT 1;
-select 'form' as component, 'Authentication' as title, 'Log in' as validate, 'create_session_token.sql' as action;
-select 'Username' as name, 'user' as prefix_icon, 'admin' as placeholder;
-select 'Password' as name, 'lock' as prefix_icon, 'admin' as placeholder, 'password' as type;
-
-select 'alert' as component, 'danger' as color, 'Invalid username or password' as title where $failed is not null;
+select
+ 'login' as component,
+ 'create_session_token.sql' as action,
+ '/assets/icon.webp' as image,
+ 'Demo Login Form' as title,
+ 'Username' as username,
+ 'Password' as password,
+ case when $failed is not null then 'Invalid username or password. In this demo, you can log in with admin / admin.' end as error_message,
+ 'In this demo, the username is "admin" and the password is "admin".' as footer_md,
+ 'Log in' as validate;
select 'text' as component, '
@@ -12,7 +17,7 @@ select 'text' as component, '
This is a simple example of an authentication form.
It uses
- - the [`form`](/documentation.sql?component=form#component) component to create a login form
+ - the [`login`](/documentation.sql?component=login#component) component to create a login form
- the [`authentication`](/documentation.sql?component=authentication#component) component to check the user password
- the [`cookie`](/documentation.sql?component=cookie#component) component to store a unique session token in the user browser
- the [`redirect`](/documentation.sql?component=redirect#component) component to redirect the user to the login page if they are not logged in
diff --git a/examples/official-site/sqlpage/migrations/07_authentication.sql b/examples/official-site/sqlpage/migrations/07_authentication.sql
index fd342fc6..c759dee8 100644
--- a/examples/official-site/sqlpage/migrations/07_authentication.sql
+++ b/examples/official-site/sqlpage/migrations/07_authentication.sql
@@ -14,7 +14,7 @@ you have two main options:
- does not require any external service
- gives you fine-grained control over
- which pages and actions are protected
- - the look of the login form
+ - the look of the [login form](?component=login)
- the duration of the session
- the permissions of each user
2. [**Single sign-on**](/sso)
@@ -128,12 +128,10 @@ Then, in all the pages that require authentication, you check if the cookie is p
You can check if the user has sent the correct password in a form, and if not, redirect them to a login page.
-Create a login form in a file called `login.sql`:
+Create a login form in a file called `login.sql` that uses the [login component](?component=login):
```sql
-select ''form'' as component, ''Authentication'' as title, ''Log in'' as validate, ''create_session_token.sql'' as action;
-select ''Username'' as name, ''admin'' as placeholder;
-select ''Password'' as name, ''admin'' as placeholder, ''password'' as type;
+select ''login'' as component;
```
And then, in `create_session_token.sql` :
diff --git a/examples/official-site/sqlpage/migrations/68_login.sql b/examples/official-site/sqlpage/migrations/68_login.sql
new file mode 100644
index 00000000..cce32fae
--- /dev/null
+++ b/examples/official-site/sqlpage/migrations/68_login.sql
@@ -0,0 +1,66 @@
+INSERT INTO component(name, icon, description, introduced_in_version) VALUES
+ ('login', 'password-user', '
+The login component is an authentication form with numerous customization options.
+It offers the main functionalities for this type of form.
+The user can enter their username and password.
+There are many optional attributes such as the use of icons on input fields, the insertion of a link to a page to reset the password, an option for the application to maintain the user''s identity via a cookie.
+It is also possible to set the title of the form, display the company logo, or customize the appearance of the form submission button.
+
+This component should be used in conjunction with other components such as [authentication](component.sql?component=authentication) and [cookie](component.sql?component=cookie).
+It does not implement any logic and simply collects the username and password to pass them to the code responsible for authentication.
+
+A few things to know :
+- The form uses the POST method to transmit information to the destination page,
+- The user''s username and password are entered into fields with the names `username` and `password`,
+- To obtain the values of username and password, you must use the variables `:username` and `:password`,
+- To know if the user wants their identity to be remembered, you must read the value of the variable `:remember`.
+', '0.39.0');
+
+INSERT INTO parameter(component, name, description, type, top_level, optional) SELECT 'login', * FROM (VALUES
+ ('title','Title of the authentication form.','TEXT',TRUE,TRUE),
+ ('enctype','Form data encoding.','TEXT',TRUE,TRUE),
+ ('action','An optional link to a target page that will handle the results of the form. ','TEXT',TRUE,TRUE),
+ ('error_message','An error message to display above the form, typically shown after a failed login attempt.','TEXT',TRUE,TRUE),
+ ('username','Label and placeholder for the user account identifier text field.','TEXT',TRUE,FALSE),
+ ('password','Label and placeholder for the password field.','TEXT',TRUE,FALSE),
+ ('username_icon','Icon to display on the left side of the input field, on the same line.','ICON',TRUE,TRUE),
+ ('password_icon','Icon to display on the left side of the input field, on the same line.','ICON',TRUE,TRUE),
+ ('image','The URL of an centered image displayed before the title.','URL',TRUE,TRUE),
+ ('forgot_password_text','A text for the link allowing the user to reset their password. If the text is empty, the link is not displayed.','TEXT',TRUE,TRUE),
+ ('forgot_password_link','The link to the page allowing the user to reset their password.','TEXT',TRUE,TRUE),
+ ('remember_me_text','A text for the option allowing the user to request the preservation of their identity. If the text is empty, the option is not displayed.','TEXT',TRUE,TRUE),
+ ('footer','A text placed at the bottom of the authentication form.','TEXT',TRUE,TRUE),
+ ('footer_md','A markdown text placed at the bottom of the authentication form. Useful for creating links to other pages (creating a new account, contacting technical support, etc.).','TEXT',TRUE,TRUE),
+ ('validate','The text to display in the button at the bottom of the form that submits the values.','TEXT',TRUE,TRUE),
+ ('validate_color','The color of the button at the bottom of the form that submits the values. Omit this property to use the default color.','COLOR',TRUE,TRUE),
+ ('validate_shape','The shape of the validation button.','TEXT',TRUE,TRUE),
+ ('validate_outline','A color to outline the validation button.','COLOR',TRUE,TRUE),
+ ('validate_size','The size of the validation button.','TEXT',TRUE,TRUE)
+) x;
+
+-- Insert example(s) for the component
+INSERT INTO example(component, description, properties)
+VALUES (
+ 'login',
+ 'Using the main options of the login component',
+ JSON(
+ '[
+ {
+ "component": "login",
+ "action": "login.sql",
+ "image": "../assets/icon.webp",
+ "title": "Please login to your account",
+ "username": "Username",
+ "password": "Password",
+ "username_icon": "user",
+ "password_icon": "lock",
+ "forgot_password_text": "Forgot your password?",
+ "forgot_password_link": "reset_password.sql",
+ "remember_me_text": "Remember me",
+ "footer_md": "Don''t have an account? [Register here](register.sql)",
+ "validate": "Sign in"
+ }
+ ]'
+ )
+ ),
+ ('login', 'Most basic login form', JSON('[{"component": "login"}]'));
diff --git a/examples/official-site/sqlpage/migrations/99_shared_id_class_attributes.sql b/examples/official-site/sqlpage/migrations/99_shared_id_class_attributes.sql
index 47e66576..df054541 100644
--- a/examples/official-site/sqlpage/migrations/99_shared_id_class_attributes.sql
+++ b/examples/official-site/sqlpage/migrations/99_shared_id_class_attributes.sql
@@ -18,7 +18,8 @@ FROM (VALUES
('title', TRUE),
('tracking', TRUE),
('text', TRUE),
- ('carousel', TRUE)
+ ('carousel', TRUE),
+ ('login', TRUE)
);
INSERT INTO parameter(component, top_level, name, description, type, optional)
@@ -49,6 +50,7 @@ FROM (VALUES
('timeline', FALSE),
('title', TRUE),
('tracking', TRUE),
- ('carousel', TRUE)
+ ('carousel', TRUE),
+ ('login', TRUE)
);
diff --git a/examples/user-authentication/docker-compose.yml b/examples/user-authentication/docker-compose.yml
index 5108ac1e..96b78c0e 100644
--- a/examples/user-authentication/docker-compose.yml
+++ b/examples/user-authentication/docker-compose.yml
@@ -1,6 +1,8 @@
services:
web:
image: lovasoa/sqlpage:main # main is cutting edge, use sqlpage/SQLPage:latest for the latest stable version
+ build:
+ context: "../.."
ports:
- "8080:8080"
volumes:
diff --git a/examples/user-authentication/signin.sql b/examples/user-authentication/signin.sql
index 4057e44d..bab0e883 100644
--- a/examples/user-authentication/signin.sql
+++ b/examples/user-authentication/signin.sql
@@ -1,14 +1,9 @@
-SELECT 'form' AS component,
+SELECT 'login' AS component,
+ 'login.sql' AS action,
'Sign in' AS title,
- 'Sign in' AS validate,
- 'login.sql' AS action;
-
-SELECT 'username' AS name;
-SELECT 'password' AS name, 'password' AS type;
-
-SELECT 'alert' as component,
- 'Sorry' as title,
- 'We could not authenticate you. Please log in or [create an account](signup.sql).' as description_md,
- 'alert-circle' as icon,
- 'red' as color
-WHERE $error IS NOT NULL;
\ No newline at end of file
+ 'Username' AS username,
+ 'Password' AS password,
+ 'user' AS username_icon,
+ 'lock' AS password_icon,
+ case when $error is not null then 'We could not authenticate you. Please log in or [create an account](signup.sql).' end as error_message_md,
+ 'Sign in' AS validate;
\ No newline at end of file
diff --git a/sqlpage/templates/login.handlebars b/sqlpage/templates/login.handlebars
new file mode 100644
index 00000000..46014bfc
--- /dev/null
+++ b/sqlpage/templates/login.handlebars
@@ -0,0 +1,82 @@
+