d718c5a372
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled
86 lines
3.7 KiB
Markdown
86 lines
3.7 KiB
Markdown
# User authentication demo
|
|
|
|
This example demonstrates how to handle user authentication with SQLpage.
|
|
|
|
It uses a PostgreSQL database to store user information and session ids,
|
|
but the same principles can be applied to other databases.
|
|
|
|
All the user and password management is done in SQLPage,
|
|
which uses [best practices](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#maximum-password-lengths) for password storage.
|
|
|
|
This demonstrates how to implement:
|
|
- [a signup form](./signup.sql)
|
|
- [a login form](./signin.sql)
|
|
- [a logout button](./logout.sql)
|
|
- [secured pages](./protected_page.sql) that can only be accessed by logged-in users
|
|
|
|
User authentication is a complex topic, and you can follow the work on implementing differenet authentication methods in [this issue](https://github.com/sqlpage/SQLPage/issues/12).
|
|
|
|
## How to run
|
|
|
|
Install [docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/).
|
|
|
|
Then run the following command in this directory:
|
|
|
|
```bash
|
|
docker-compose up
|
|
```
|
|
|
|
Then open [http://localhost:8080](http://localhost:8080) in your browser.
|
|
|
|
## Caveats
|
|
|
|
In this example, we handle user creation and login in SQLpage.
|
|
|
|
If you are implementing user authentication in an public application with potentially sensitive data,
|
|
you should propably read the [Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) from OWASP.
|
|
|
|
## Screenshots
|
|
|
|
| Signup form | Login form | Protected page |
|
|
| --- | --- | --- |
|
|
|  |  |  |
|
|
|  |  |  |
|
|
|
|
## How it works
|
|
|
|
### User creation
|
|
|
|
The [signup form](./signup.sql) is a simple form that is handled by [`create_user.sql`](./create_user.sql).
|
|
You could restrict user creation to existing administrators and create an initial administrator in a database migration.
|
|
|
|
### User login
|
|
|
|
The [login form](./signin.sql) is a simple form that is handled by [`login.sql`](./login.sql).
|
|
|
|
`login.sql` checks that the username exists and that the password is correct using the [authentication component](https://sql-page.com/documentation.sql?component=authentication#component) extension with
|
|
|
|
```sql
|
|
SELECT 'authentication' AS component,
|
|
'signin.sql' AS link,
|
|
(SELECT password_hash FROM user_info WHERE username = :username) AS password_hash,
|
|
:password AS password;
|
|
```
|
|
|
|
If the login is successful, an entry is added to the [`login_session`](./sqlpage/migrations/0000_init.sql) table with a random session id.
|
|
|
|
If it is not, the authentication component will redirect the user to the login page and stop the execution of the page.
|
|
|
|
The session id is then stored in a cookie on the user's browser.
|
|
|
|
The user is then redirected to [`./protected_page.sql`](./protected_page.sql) which will check that the user is logged in.
|
|
|
|
### Protected pages
|
|
|
|
Protected pages are pages that can only be accessed by logged-in users.
|
|
|
|
There is an example in [`protected_page.sql`](./protected_page.sql) that uses
|
|
the [`redirect`](https://sql-page.com/documentation.sql?component=redirect#component)
|
|
component to redirect the user to the login page if they are not logged in.
|
|
|
|
Checking whether the user is logged in is as simple as checking that session id returned by [`sqlpage.cookie('session')`](https://sql-page.com/functions.sql?function=cookie#function) exists in the [`login_session`](./sqlpage/migrations/0000_init.sql) table.
|
|
|
|
|
|
### User logout
|
|
|
|
The cookie can be deleted in the browser by navigating to [`./logout.sql`](./logout.sql). |