{"id":23407651,"url":"https://github.com/bestia-dev/authentication_database_web_ui","last_synced_at":"2025-04-09T00:49:12.224Z","repository":{"id":105010500,"uuid":"554678621","full_name":"bestia-dev/authentication_database_web_ui","owner":"bestia-dev","description":"09. Tutorial to code authentication for database_web_ui (2022-10)","archived":false,"fork":false,"pushed_at":"2025-03-29T15:46:55.000Z","size":2791,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T16:30:04.710Z","etag":null,"topics":["obsolete","tutorial"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bestia-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-20T07:51:33.000Z","updated_at":"2025-03-29T15:46:58.000Z","dependencies_parsed_at":"2025-02-13T14:15:26.395Z","dependency_job_id":null,"html_url":"https://github.com/bestia-dev/authentication_database_web_ui","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bestia-dev%2Fauthentication_database_web_ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bestia-dev%2Fauthentication_database_web_ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bestia-dev%2Fauthentication_database_web_ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bestia-dev%2Fauthentication_database_web_ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bestia-dev","download_url":"https://codeload.github.com/bestia-dev/authentication_database_web_ui/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247953090,"owners_count":21023945,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["obsolete","tutorial"],"created_at":"2024-12-22T14:29:15.935Z","updated_at":"2025-04-09T00:49:12.192Z","avatar_url":"https://github.com/bestia-dev.png","language":"Rust","readme":"\u003c!-- markdownlint-disable MD041 --\u003e\n[//]: # (auto_md_to_doc_comments segment start A)\n\n# authentication_database_web_ui\n\n[//]: # (auto_cargo_toml_to_md start)\n\n**09. Tutorial to code authentication for database_web_ui (2022-10)**  \n***version: 0.1.01 date: 2022-10-10 author: [bestia.dev](https://bestia.dev) repository: [GitHub](https://github.com/bestia-dev/authentication_database_web_ui)***  \n\n[//]: # (auto_cargo_toml_to_md end)\n\n[//]: # (auto_github_topics start)\n\n ![obsolete](https://img.shields.io/badge/obsolete-red)\n ![tutorial](https://img.shields.io/badge/tutorial-yellow)\n\n[//]: # (auto_github_topics end)\n\n[//]: # (auto_lines_of_code start)\n\n[![Lines in Rust code](https://img.shields.io/badge/Lines_in_Rust-230-green.svg)](https://github.com/bestia-dev/authentication_database_web_ui/)\n[![Lines in Doc comments](https://img.shields.io/badge/Lines_in_Doc_comments-9-blue.svg)](https://github.com/bestia-dev/authentication_database_web_ui/)\n[![Lines in Comments](https://img.shields.io/badge/Lines_in_comments-28-purple.svg)](https://github.com/bestia-dev/authentication_database_web_ui/)\n[![Lines in examples](https://img.shields.io/badge/Lines_in_examples-0-yellow.svg)](https://github.com/bestia-dev/authentication_database_web_ui/)\n[![Lines in tests](https://img.shields.io/badge/Lines_in_tests-0-orange.svg)](https://github.com/bestia-dev/authentication_database_web_ui/)\n\n[//]: # (auto_lines_of_code end)\n\n [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bestia-dev/authentication_database_web_ui/blob/main/LICENSE)\n ![authentication_database_web_ui](https://bestia.dev/webpage_hit_counter/get_svg_image/12164623.svg)\n\nHashtags: #rust #rustlang #tutorial  \nMy projects on Github are more like a tutorial than a finished product: [bestia-dev tutorials](https://github.com/bestia-dev/tutorials_rust_wasm).\n\n## OBSOLETE\n\nThis topic is far too large for a single repository. The coding is continued in the Github organization \u003chttps://github.com/orgs/liporuwcha\u003e\n\n## Intro\n\nIn [previous tutorials](https://github.com/bestia-dev/refactoring_database_web_ui_server_side_rendering), we created a working prototype for a web application that can create, read, update and delete (CRUD) data in a Postgres database.  \nWe will continue to develop this project in the 9th part of the [Rust tutorial series](https://www.youtube.com/channel/UCitt3zFHK2jDetDh6ezI05A). Today, we will add some code for user authentication.  \n\n## Motivation\n\nIf a web application manipulates data in a database it needs some kind of authentication. You can not leave it completely open to the internet. All kinds of nasty things can occur. It is a World Wild West out there!\nAuthentication is the process of recognizing the user's identity. This is later used to authorize the user to what can he/she do with the database.  \n\n## Authentication and session cookie\n\nThe topic of authentication can get very complicated very quickly. For this tutorial, we will use a very \"simple\" method with sessions and cookies. It sounds old-school and it is, but it is simple, effective and it has been strengthened lately. All communication must be secured by SSL/TLS because otherwise a hacker can intercept and read secrets. The cookies MUST be SameSite to avoid nasty hacker attacks. This session cookie is a \"necessary cookie\" and is exempt from the European Privacy GDPR Laws.\n\n## Signup\n\nA new user will signup on a \"user_signup page\" with 3 input strings: user_email and 2 times the same password.  \nThe client (WASM) checks that the fields are not empty, if the user_email looks like an email, if the 2 passwords are identical and if they are complicated enough.  \nThe client sends the email to the server, where it checks that it does not already exist as a login and it is allowed by some rules. The server returns the salt that is just a random uuid.\nThe password is salted and hashed as described in the mechanism [SCRAM](https://en.wikipedia.org/wiki/Salted_Challenge_Response_Authentication_Mechanism).  \nThen the client can calculates the hash with this salt. The user_email and hashed password are sent to the server. This way only the user knows the password. The administrator or hacker will see only the salted hash. The data is temporarily stored in table \"user_signup\".  \nThe server sends an email to the user with a link for verification. After verification, the record is moved to the table b2_authn_login. The use can now login.  \n\n## Login\n\nThe user opens the \"b2_authn_login\" page with 2 input strings: user_email and password. The client checks that the fields are not empty and if the user_email looks like an email. The client sends the first msg to the server with the user_email. The server reads the hash from the login table and returns the salt for hashing. The client calculates the salted hash and sends it. The server checks if the hash is the same as in the database. If not, a red alert is shown.  \nIf successful, the server inserts a new random session_id into the table user_session and sends a cookie to the client. The session is ephemeral. It expires after 5 minutes of the last request.  \n\n## Check session cookie\n\nThis session cookie will be attached to every request sent from this client. The server will check in the table user_session that the session_id is alive and grant access. The session_id has an expiration date_time and this is updated on every request. If the session_id expires it is deleted from the table and subsequent requests will fail. The user will need to login again. Session_id does not need to be saved in a persistent table for now. I will use some kind of cache in memory for performance. Alternatively, we could use [Redis distributed cache](https://redis.io/), which is faster than database access.\n\n## Authorization\n\nThe main goal of authentication is authorization. We can give different users different permissions/authorizations.  \nAny request from application must be authorized to ensure that the current user has appropriate permissions to make the request.\nThe authorization is based on the `a2_authorization_role`. Every login must be in one or more roles. The basic role is `user`.\n\n[//]: # (auto_plantuml start)\n\u003c!-- markdownlint-disable MD033 --\u003e\n\u003cdetails\u003e\u003csummary\u003eplantuml code:\u003c/summary\u003e\n\u003c!-- markdownlint-enable MD033 --\u003e\n\n```plantuml\n@startuml name = \"plantuml_authorization\"\n\nb2_authn_login ||--o{ b3_authn_login_role\nb3_authn_role ||--o{ b3_authn_login_role\n\nnote bottom\n  many-to-many relations\nend note\n\nb3_authn_role ||--o{ b3_authn_role_permission\nb2_authn_permission ||--o{ b3_authn_role_permission\n\nnote bottom\n  many-to-many relations\nend note\n\n@enduml\n```\n\n\u003c/details\u003e\n\n![plantuml_authorization](https://github.com/bestia-dev/authentication_database_web_ui-/raw/main/images/plantuml_authorization.png)\n\n[//]: # (auto_plantuml end)\n\n## Sending email\n\nIt is not easy to send emails over SMTP anymore because of spam. There is so much spam, that big email providers invented the \"email deliverability reputation\" system, which makes it difficult for smaller senders to not be flagged as spam. So the solution is to use some free email providers like MailGun, MailJet, Mailersend or Sendgrid. The email communication between a web app and its user is called \"transactional email\" and is specific because it needs to be fast and reliable. A \"transactional email\" is a type of email message that’s triggered by a specific action on a website or mobile app. Some common examples of transactional emails include password resets, account notifications, welcome emails, and any other confirmation emails that are sent via automation. These automated emails are typically sent programmatically through an email API.  \nI will try Twilio Sendgrid free plan with 100 emails per day forever. Enough for developing web apps. I will be using Web API v3. I will create the json in code and send to the API point with [reqwest](https://docs.rs/reqwest/latest/reqwest/).  \nFirst, on \u003chttp://sendgrid.com\u003e create an API key with restricted permissions just to send emails.  \nThis secret will be stored in the `.env` file, but encrypted with the master_key for this application.  \nThe verification of my \"single sender identity\" was not really helpful. When I sent an email it was marked as spam. I then tried with the better \"domain verification\". I added the 3 CNAME records from Sendgrid to my domain dns on \u003chttp://porkbun.com\u003e. I then used \u003chttps://mxtoolbox.com\u003e to check the CNAME records. It is not enough to write just \"bestia.dev\", I needed to write the whole CNAME host \"s1._domainkey.bestia.dev\" or \"s2._domainkey.bestia.dev\". The most interesting is \"em4619.bestia.dev\" because the number changes for every attempt to \"domain verification\". Now I have verified my domain. Good. It works perfectly !\n\n\n\n## Debugger\n\nI want to debug the tier2 web app inside VSCode. I want to see the variables and what is going on. The debugger inside VSCode should be better than \"print debugging\".  \nIn VSCode install the extension CodeLLDB.  \nThe container must be run with some config that allows debugging.\n\n```bash\n--cap-add=SYS_PTRACE --security-opt seccomp=unconfined\n```\n\nI created a new \"pod\" with these config called \"pod_with_rust_pg_vscode_debugger\". The container images are unchanged by this.\nYes, the debugger needs some pretty high privileges and seccomp is disabled, but still I think that the isolation with the container is better than without it.\n\n\n\n\n## Database, modules and namespaces\n\nIt is short-sighted to treat all the database as a monolith.  \nUsually real life is complicated and we have different modules in the database that are developed by different teams and are loosely-coupled together. Inside one module things are very interconnected, but between different modules we try to connect them as little as possible.  \nIt is wise to have a strict hierarchy where higher level modules can call lower levels, but not reverse. Maybe there is some clever way to separate objects with schemas, but I will do it the old-school way. I will add a namespace prefix. It is absolutely ugly as hell, but effective. And no surprises here.  \nIt is also very handy for search the same name in the entire workspace with many different languages and modules. The name becomes so specific that there cannot be false positives at all.  \nThe lowest objects will be level \"a\", then \"b\", \"c\" and so on. Inside the levels the components are numbered example: a1, a2, a3...\n\n[//]: # (auto_plantuml start)\n\u003c!-- markdownlint-disable MD033 --\u003e\n\u003cdetails\u003e\u003csummary\u003eplantuml code:\u003c/summary\u003e\n\u003c!-- markdownlint-enable MD033 --\u003e\n\n```plantuml\n@startuml name = \"plantuml_database_objects\"\ntop to bottom direction\nskinparam componentStyle rectangle\n\ncomponent \"database objects\" as x{\n    component \"Level c\" as c {\n        [c1_webpage_hits_mod]\n    }\n\n    component \"Level b\" as b {\n        [b1_authn_signup_mod]\n        [b2_authn_login_mod]   \n    }    \n    [b1_authn_signup_mod] -[hidden]\u003e [b2_authn_login_mod]\n\n    component \"Level a\" as a {\n        [a0_init_mod]\n        [a1_list_mod]\n        [a2_migrate_mod]\n        [a3_check_mod]\n        [a4_system_mod] \n    }    \n    [a0_init_mod] -[hidden]\u003e [a1_list_mod]\n    [a1_list_mod] -[hidden]\u003e [a2_migrate_mod]\n    [a2_migrate_mod] -[hidden]\u003e [a3_check_mod]\n    [a3_check_mod] -[hidden]\u003e [a4_system_mod]\n\n    component [Postgres] as p {\n        [tables]\n        [constraints]\n        [views]\n        [functions]\n    }\n    [tables] -[hidden]\u003e [constraints]\n    [constraints] -[hidden]\u003e [views]\n    [views] -[hidden]\u003e [functions]\n}\n\nc -d-\u003e b\nb -d-\u003e a\na -d-\u003e p\n\n@enduml\n```\n\n\u003c/details\u003e\n\n![plantuml_database_objects](https://github.com/bestia-dev/authentication_database_web_ui-/raw/main/images/plantuml_database_objects.png)\n\n[//]: # (auto_plantuml end)\n\n\n\n## html templates\n\nThe html files are saved in the \"web_server_folder\" with the same hierarchy as the Rust modules or SQL files. Again, the crucial point is to have the same unique name everywhere. Without it, we would be just lost in the sea of code.  \nThe html files must be saved in UTF-8 encoding without the byte-order mark (BOM).  \nA lot of the html files are very similar with just a few differences. This could be optimized deduplicating the code somehow.\n\n## secrets\n\nI will encrypt/decrypt secrets (passwords, API secret_tokens and keys) inside the `.env` file.  \nThe master key will be in ~/.ssh/ directory like other private keys. The name will be webpage_hits_admin.master_key.  \n\n```bash\nnano ~/.ssh/webpage_hits_admin.master_key\nchmod 600 ~/.ssh/webpage_hits_admin.master_key\n```\n\n## Other observations\n\nIf there is the problem that cargo rebuilds a bunch of dependencies over and over again, we can debug the rebuilds with the env variable:  \n\n```bash\nCARGO_LOG=cargo::core::compiler::fingerprint=info cargo build\n```\n\n## Open-source and free as a beer\n\nMy open-source projects are free as a beer (MIT license).  \nI just love programming.  \nBut I need also to drink. If you find my projects and tutorials helpful, please buy me a beer by donating to my [PayPal](https://paypal.me/LucianoBestia).  \nYou know the price of a beer in your local bar ;-)  \nSo I can drink a free beer for your health :-)  \n[Na zdravje!](https://translate.google.com/?hl=en\u0026sl=sl\u0026tl=en\u0026text=Na%20zdravje\u0026op=translate) [Alla salute!](https://dictionary.cambridge.org/dictionary/italian-english/alla-salute) [Prost!](https://dictionary.cambridge.org/dictionary/german-english/prost) [Nazdravlje!](https://matadornetwork.com/nights/how-to-say-cheers-in-50-languages/) 🍻\n\n[//bestia.dev](https://bestia.dev)  \n[//github.com/bestia-dev](https://github.com/bestia-dev)  \n[//bestiadev.substack.com](https://bestiadev.substack.com)  \n[//youtube.com/@bestia-dev-tutorials](https://youtube.com/@bestia-dev-tutorials)  \n\n[//]: # (auto_md_to_doc_comments segment end A)\n","funding_links":["https://paypal.me/LucianoBestia"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbestia-dev%2Fauthentication_database_web_ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbestia-dev%2Fauthentication_database_web_ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbestia-dev%2Fauthentication_database_web_ui/lists"}