Skip to content

Commit a128ad0

Browse files
committed
add an authentication example
update documentation
1 parent 64a8f09 commit a128ad0

File tree

8 files changed

+113
-62
lines changed

8 files changed

+113
-62
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@
2121
- datagrids are now slightly more compact, with less padding and less space taken by each item.
2222
- fix a bug in the [card](https://sql.ophir.dev/documentation.sql?component=card#component) component where the icon would sometimes overflow the card's text content.
2323
- new `image` property in the [button](https://sql.ophir.dev/documentation.sql?component=button#component) component to display a small image inside a button.
24+
- In the `shell` component
25+
- allow easily creating complex menus even in SQLite:
26+
```sql
27+
select 'shell' as component, 'My Website' as title, '{"title":"About","submenu":[{"link":"/x.sql","title":"X"},{"link":"/y.sql","title":"Y"}]}') as menu_item;
28+
```
29+
- allow easily creating optional menu items that are only displayed in some conditions:
30+
```sql
31+
select 'shell' as component, 'My Website' as title, CASE WHEN $role = 'admin' THEN 'Admin' END as menu_item;
32+
```
33+
2434

2535
## 0.23.0 (2024-06-09)
2636

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- delete expired sessions
2+
delete from user_sessions where created_at < datetime('now', '-1 day');
3+
4+
-- check that the
5+
SELECT 'authentication' AS component,
6+
'login.sql?failed' AS link, -- redirect to the login page on error
7+
(SELECT password_hash FROM users WHERE username = :Username) AS password_hash, -- this is a hash of the password 'admin'
8+
:Password AS password; -- this is the password that the user sent through our form in 'index.sql'
9+
10+
-- if we haven't been redirected, then the password is correct
11+
-- create a new session
12+
insert into user_sessions (session_token, username) values (sqlpage.random_string(32), :Username)
13+
returning 'cookie' as component, 'session_token' as name, session_token as value;
14+
15+
-- redirect to the authentication example home page
16+
select 'redirect' as component, '/examples/authentication' as link;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- redirect the user to the login page if they are not logged in
2+
-- this query should be present at the top of every page that requires authentication
3+
set $role = (select role from users natural join user_sessions where session_token = sqlpage.cookie('session_token'));
4+
select 'redirect' as component, 'login.sql' as link where $role is null;
5+
6+
select 'dynamic' as component,
7+
json_insert(properties, '$[0].menu_item[#]', 'logout') as properties
8+
FROM example WHERE component = 'shell' LIMIT 1;
9+
10+
select 'alert' as component, 'info' as color, CONCAT('You are logged in as ', $role) as title;
11+
12+
select 'text' as component, '
13+
# Authentication
14+
15+
Read the [source code](//github.com/lovasoa/SQLpage/blob/main/examples/official-site/examples/authentication/) for this demo.
16+
17+
[Log out](logout.sql)
18+
' as contents_md;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
select 'dynamic' as component, properties FROM example WHERE component = 'shell' LIMIT 1;
2+
3+
select 'form' as component, 'Authentication' as title, 'Log in' as validate, 'create_session_token.sql' as action;
4+
select 'Username' as name, 'user' as prefix_icon, 'admin' as placeholder;
5+
select 'Password' as name, 'lock' as prefix_icon, 'admin' as placeholder, 'password' as type;
6+
7+
select 'alert' as component, 'danger' as color, 'Invalid username or password' as title where $failed is not null;
8+
9+
select 'text' as component, '
10+
11+
# Authentication
12+
13+
This is a simple example of an authentication form.
14+
It uses
15+
- the [`form`](/documentation.sql?component=form#component) component to create a login form
16+
- the [`authentication`](/documentation.sql?component=authentication#component) component to check the user password
17+
- the [`cookie`](/documentation.sql?component=cookie#component) component to store a unique session token in the user browser
18+
- the [`redirect`](/documentation.sql?component=redirect#component) component to redirect the user to the login page if they are not logged in
19+
20+
## Example credentials
21+
22+
- Username: `admin`, Password: `admin`
23+
- Username: `user`, Password: `user`
24+
' as contents_md;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
delete from user_sessions
2+
where session_token = sqlpage.cookie('session_token');
3+
4+
select 'redirect' as component, 'login.sql' as link;

examples/official-site/examples/dynamic_menu.sql

Lines changed: 0 additions & 29 deletions
This file was deleted.

examples/official-site/sqlpage/migrations/01_documentation.sql

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ You see the [page layouts demo](./examples/layouts.sql) for a live example of th
816816
{"link": "/examples/layouts.sql", "title": "Layouts"},
817817
{"link": "/examples/multistep-form", "title": "Forms"},
818818
{"link": "/examples/handle_picture_upload.sql", "title": "File uploads"},
819-
{"link": "/examples/hash_password.sql", "title": "Password protection"},
819+
{"link": "/examples/authentication/", "title": "Password protection"},
820820
{"link": "//github.com/lovasoa/SQLpage/blob/main/examples/", "title": "All examples & demos"}
821821
]},
822822
{"title": "Community", "submenu": [
@@ -899,46 +899,36 @@ FROM my_menu_items
899899
900900
(check your database documentation for the exact syntax of the `json_group_array` function).
901901
902-
Another "dynamic" aspect is when menu items are adjusted based on the environment. For example,
903-
Logout/UserProfile buttons may be presented to an authenticated user and Login/SignUp, otherwise
904-
(such an example will presented in a separate demo). The following simple example demonstrates
905-
the concept by hiding one arbitrary menu when the page is loaded. Then you can select from a
906-
dropdown menu, which menu to hide. To hide a menu item, return NULL or empty JSON object ''{}''
907-
as demonstrated below.
902+
Another case when dynamic menus are useful is when you want to show some
903+
menu items only in certain conditions.
904+
905+
For instance, you could show an "Admin panel" menu item only to users with the "admin" role,
906+
a "Profile" menu item only to authenticated users,
907+
and a "Login" menu item only to unauthenticated users:
908908
909909
```sql
910-
SET $dummy = ifnull(:menu, abs(random()) % 5);
910+
SET $role = (
911+
SELECT role FROM users
912+
INNER JOIN sessions ON users.id = sessions.user_id
913+
WHERE sessions.session_id = sqlpage.cookie(''session_id'')
914+
); -- Read more about how to handle user sessions in the "authentication" component documentation
911915
912916
SELECT
913-
''shell'' AS component,
914-
''SQLPage'' AS title,
915-
''database'' AS icon,
916-
''/'' AS link,
917-
iif($dummy = 1, NULL, ''{"title":"About","submenu":[{"link":"/safety.sql","title":"Security"},{"link":"/performance.sql","title":"Performance"}]}'') AS menu_item,
918-
iif($dummy = 2, ''{}'', ''{"title":"Examples","submenu":[{"link":"/examples/tabs.sql","title":"Tabs"},{"link":"/examples/layouts.sql","title":"Layouts"}]}'') AS menu_item,
919-
iif($dummy = 3, NULL, ''{"title":"Community","submenu":[{"link":"blog.sql","title":"Blog"},{"link":"//github.com/lovasoa/sqlpage/issues","title":"Report a bug"}]}'') AS menu_item,
920-
iif($dummy = 4, ''{}'', ''{"title":"Documentation","submenu":[{"link":"/your-first-sql-website","title":"Getting started"},{"link":"/components.sql","title":"All Components"}]}'') AS menu_item,
921-
''Official [SQLPage](https://sql.ophir.dev) documentation'' as footer;
917+
''shell'' AS component,
918+
''My authenticated website'' AS title,
922919
920+
-- Add an admin panel link if the user is an admin
921+
CASE WHEN $role = ''admin'' THEN ''{"link": "admin.sql", "title": "Admin panel"}'' END AS menu_item,
923922
924-
SELECT
925-
''form'' AS component,
926-
''Hide Menu'' AS validate,
927-
sqlpage.path() AS action;
928-
SELECT
929-
''select'' AS type,
930-
''menu'' AS name,
931-
''Hide Menu'' AS label,
932-
2 AS width,
933-
CAST($dummy AS INT) AS value,
934-
''[{"label": "None", "value": 0},
935-
{"label": "About", "value": 1},
936-
{"label": "Examples", "value": 2},
937-
{"label": "Community", "value": 3},
938-
{"label": "Documentation", "value": 4}]'' AS options;
923+
-- Add a profile page if the user is authenticated
924+
CASE WHEN $role IS NOT NULL THEN ''{"link": "profile.sql", "title": "My profile"}'' END AS menu_item,
925+
926+
-- Add a login link if the user is not authenticated
927+
CASE WHEN $role IS NULL THEN ''login'' END AS menu_item
928+
;
939929
```
940930
941-
Follow [this link](examples/dynamic_menu.sql) to try this code.
931+
More about how to handle user sessions in the [authentication component documentation](?component=authentication#component).
942932
', NULL),
943933
('shell', '
944934
### A page without a shell
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
create table users (
2+
username text primary key,
3+
password_hash text not null,
4+
role text not null
5+
);
6+
7+
-- Create example users with trivial passwords for the website's demo
8+
insert into users (username, password_hash, role)
9+
values
10+
('admin', '$argon2i$v=19$m=8,t=1,p=1$YWFhYWFhYWE$ROyXNhK0utkzTA', 'admin'), -- password: admin
11+
('user', '$argon2i$v=19$m=8,t=1,p=1$YWFhYWFhYWE$qsrWdjgl96ooYw', 'user'); -- password: user
12+
-- (the password hashes can be generated using the `sqlpage.hash_password` function)
13+
14+
create table user_sessions (
15+
session_token text primary key,
16+
username text not null references users(username),
17+
created_at timestamp not null default current_timestamp
18+
);

0 commit comments

Comments
 (0)