Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- <img width="1250" height="1263" alt="image" src="https://github.com/user-attachments/assets/6781a31f-e342-4e8c-8506-bc47049ce313" />
- 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 11 additions & 6 deletions examples/official-site/examples/authentication/login.sql
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
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, '

# Authentication

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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` :
Expand Down
66 changes: 66 additions & 0 deletions examples/official-site/sqlpage/migrations/68_login.sql
Original file line number Diff line number Diff line change
@@ -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"}]'));
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -49,6 +50,7 @@ FROM (VALUES
('timeline', FALSE),
('title', TRUE),
('tracking', TRUE),
('carousel', TRUE)
('carousel', TRUE),
('login', TRUE)
);

2 changes: 2 additions & 0 deletions examples/user-authentication/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
21 changes: 8 additions & 13 deletions examples/user-authentication/signin.sql
Original file line number Diff line number Diff line change
@@ -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;
'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;
82 changes: 82 additions & 0 deletions sqlpage/templates/login.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<div class="container" {{class}}>
<div class="row d-flex justify-content-center">
<div class="col-md-6">
<form
{{#if id}}id="{{id}}"{{/if}}
class="mt-3 mb-1"
method="post"
{{#if enctype}}enctype="{{enctype}}"{{/if}}
{{#if action}}
action="{{action}}"
{{else}}
{{#if id}}action="#{{id}}"{{/if}}
{{/if}}
>
{{#if image}}
<div class="form-group d-flex justify-content-center align-items-center mb-2">
<img src="{{image}}"/>
</div>
{{/if}}
{{#if title}}
<h1 class="text-center mb-3">{{title}}</h1>
{{/if}}
{{#if (or error_message error_message_md)}}
<div class="alert alert-danger mb-3" role="alert">
<div class="alert-icon">
{{icon_img 'alert-circle'}}
</div>
<div class="overflow-auto w-100 remove-bottom-margin">
{{~error_message~}}
{{{~markdown error_message_md~}}}
</div>
</div>
{{/if}}
<label class="form-label" for="username">{{username}}</label>
<div class="input-icon mb-3">
<span class="input-icon-addon">{{icon_img (default username_icon 'user-circle')}}</span>
<input type="text" name="username" id="username" value="" class="form-control" placeholder="{{username}}" required="required" autofocus autocomplete="username"/>
</div>
<label class="form-label" for="password">{{password}}
{{#if forgot_password_text}}
<span class="form-label-description"><a href="{{forgot_password_link}}">{{forgot_password_text}}</a></span>
{{/if}}
</label>
<div class="input-icon mb-3">
<span class="input-icon-addon">{{~icon_img (default password_icon 'key')~}}</span>
<input type="password" name="password" id="password" value="" class="form-control" placeholder="{{password}}" required="required" autocomplete="current-password" />
</div>
{{#if remember_me_text}}
<label class="form-check">
<input class="form-check-input" id="remember" name="remember" type="checkbox" />
<span class="form-check-label">{{remember_me_text}}</span>
</label>
{{/if}}
<div class="form-group d-flex justify-content-center align-items-center">
<input
class="btn
btn-{{default validate_color "primary"}}
{{#if validate_shape}} btn-{{validate_shape}} {{/if}}
{{#if validate_outline}} btn-outline-{{validate_outline}} {{/if}}
{{#if validate_size}} btn-{{validate_size}} {{/if}}"
type="submit"
name="submit"
value="{{default validate 'Login'}}"/>
</div>
{{#if (or footer footer_md)}}
<hr>
<div class="text-center mb-0">
<small class="form-hint mt-0">
{{#if footer}}
{{footer}}
{{else}}
{{#if footer_md}}
{{{markdown footer_md}}}
{{/if}}
{{/if}}
</small>
</div>
{{/if}}
</form>
</div>
</div>
</div>