{"id":15177032,"url":"https://github.com/snapp-incubator/soteria","last_synced_at":"2025-07-30T14:33:12.423Z","repository":{"id":201187478,"uuid":"707163347","full_name":"snapp-incubator/soteria","owner":"snapp-incubator","description":"Snapp EMQ Authentication based on EMQ HTTP Plugin","archived":false,"fork":false,"pushed_at":"2025-07-21T06:24:17.000Z","size":13110,"stargazers_count":17,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-07-21T08:24:46.899Z","etag":null,"topics":["authentication","authorization","cloud","emqx","hacktoberfest","mqtt"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/snapp-incubator.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,"zenodo":null}},"created_at":"2023-10-19T10:53:29.000Z","updated_at":"2025-07-21T06:22:34.000Z","dependencies_parsed_at":"2024-02-12T05:24:06.014Z","dependency_job_id":"717cb3d6-c9d4-4879-b00e-29c47436ae18","html_url":"https://github.com/snapp-incubator/soteria","commit_stats":{"total_commits":995,"total_committers":22,"mean_commits":45.22727272727273,"dds":0.5417085427135678,"last_synced_commit":"a6f5d7ed7b74907e0084315acccef8b3b0db5820"},"previous_names":["snapp-incubator/soteria"],"tags_count":82,"template":false,"template_full_name":null,"purl":"pkg:github/snapp-incubator/soteria","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapp-incubator%2Fsoteria","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapp-incubator%2Fsoteria/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapp-incubator%2Fsoteria/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapp-incubator%2Fsoteria/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/snapp-incubator","download_url":"https://codeload.github.com/snapp-incubator/soteria/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapp-incubator%2Fsoteria/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267882820,"owners_count":24160212,"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","status":"online","status_checked_at":"2025-07-30T02:00:09.044Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["authentication","authorization","cloud","emqx","hacktoberfest","mqtt"],"created_at":"2024-09-27T14:01:28.162Z","updated_at":"2025-07-30T14:33:12.216Z","avatar_url":"https://github.com/snapp-incubator.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e Soteria \u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"./.github/assets/logo.jpg\" height=\"250px\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"GitHub Workflow Status\" src=\"https://img.shields.io/github/actions/workflow/status/snapp-incubator/soteria/test.yaml?logo=github\u0026style=for-the-badge\"\u003e\n    \u003cimg alt=\"Codecov\" src=\"https://img.shields.io/codecov/c/github/snapp-incubator/soteria?logo=codecov\u0026style=for-the-badge\"\u003e\n    \u003cimg alt=\"GitHub repo size\" src=\"https://img.shields.io/github/repo-size/snapp-incubator/soteria?logo=github\u0026style=for-the-badge\"\u003e\n    \u003cimg alt=\"GitHub tag (with filter)\" src=\"https://img.shields.io/github/v/tag/snapp-incubator/soteria?style=for-the-badge\u0026logo=git\"\u003e\n    \u003cimg alt=\"GitHub go.mod Go version (subdirectory of monorepo)\" src=\"https://img.shields.io/github/go-mod/go-version/snapp-incubator/soteria?style=for-the-badge\u0026logo=go\"\u003e\n\u003c/p\u003e\n\n## Introduction\n\nSoteria is responsible for Authentication and Authorization of every request sent to [EMQ](https://github.com/emqx/emqx/).\nThe following configuration in [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md)\nformat, configure EMQ to use HTTP Service for Authentication and Authorization.\n\n```hocon\n{\n    mechanism = password_based\n    backend = http\n    enable = true\n\n    method = post\n    url = \"http://127.0.0.1:8080/v2/auth\"\n    body {\n        username = \"${username}\"\n        password = \"${password}\"\n        token = \"${username}\"\n        clientid = \"${clientid}\"\n    }\n    headers {\n        \"Content-Type\" = \"application/json\"\n        \"X-Request-Source\" = \"EMQX\"\n    }\n}\n```\n\n```hocon\n{\n    type = http\n    enable = true\n\n    method = post\n    url = \"http://127.0.0.1:32333/v2/acl\"\n    body {\n        username = \"${username}\"\n        topic = \"${topic}\"\n        action = \"${action}\"\n    }\n    headers {\n        \"Content-Type\" = \"application/json\"\n        \"X-Request-Source\" = \"EMQX\"\n    }\n}\n\n```\n\nWe are using the [Authentication HTTP Service](https://www.emqx.io/docs/en/v5.2/access-control/authn/http.html)\nand [Authorization HTTP Service](https://www.emqx.io/docs/en/v5.2/access-control/authn/http.html)\nplugins of EMQ for forwarding these requests to Soteria and doing Authentication and Authorization.\nEMQ has caching mechanism, but it sends requests almost for each Publish message to Soteria.\nPS: On Subscribe we have only one message from client that need authorization and other messages are coming from server.\n\n## Architecture\n\n![arch](docs/arch.png)\n\n## Support Vendors\n\nSoteria supports having multiple vendors at the same time.\nMeans you can use single cluster for multiple companies at the same time and validate their tokens\nand control accesses.\n\n### Vendor Configuration\n\n```yaml\ncompany: \"\u003c\u003ccompany_name\u003e\u003e\"\ndriver_salt: \"\"\npassenger_salt: \"\"\npassenger_hash_length: 15\ndriver_hash_length: 15\nallowed_access_types: [\"pub\", \"sub\"]\nkeys:\n  iss-0: \"key-value\"\n  iss-1: \"key-value\"\niss_entity_map:\n  0: \"entity-0\"\n  1: \"entity-1\"\n  default: \"default-entity\"\niss_peer_map:\n  0: \"peer-0\"\n  1: \"peer-1\"\n  default: \"default-peer\"\njwt:\n  iss_name: \"iss\"\n  sub_name: \"sub\"\n  signing_method: \"RS512\"\ntopics:\n  - topic1\n  - topic2\n  - ...\n```\n\n### HashID Manager\n\n`driver_salt`,`passenger_salt`, `passenger_hash_length`, `driver_hash_length` are used for HashIDManager.\nThis component only works for passenger and driver issuers.\n\n### Keys\n\nThe following is a mapping that associates vendors (companies) with the keys used for opening JWT tokens.\nIf symmetrical keys are utilized, it is important to use their base64 representation.\nIt should also be noted that Soteria only requires public keys in cases where asymmetrical keys are employed.\n\n### IssEntityMap \u0026 IssPeerMap\n\nThese two configuration map iss to entity and peer respectively.\n\n**Note**: default case is `required`\n\n```yaml\niss_entity_map:\n  0: \"driver\"\n  1: \"passenger\"\n  default: \"none\"\niss_peer_map:\n  0: \"passenger\"\n  1: \"driver\"\n  default: \"none\"\n```\n\nIn the example above, we have two maps for entity \u0026 peer maps. As it's clear for **entity** structure **0** and **1** is mapped to **driver** and **passenger**, respectively. Vice Versa, for peer structure it can be seen that **1** and **0** is mapped to **driver** and **passenger**. We have also the **default** key for both two cases.\n\nIn the topic example, we have an accesses section in which **0** is mapped to **2** and **1** is mapped to **-1** which can be interpreted as a map from **IssEntity's Keys** to **Access Types**. In the other words this structure means:\n\n- **Driver** has a **Pub** access on topic\n- **Passenger** has a **None** access on topic (No Access)\n\n### JWT\n\nThis is the JWT configuration. `iss_name` and `sub_name` are the name of issuer\nand subject in the JWT token's payload respectively.\n\n`signing_method` is the method that is used to sign the JWT token.\nHere are list of different signing methods\n\n- ES384\n- RS512 \\*\n- PS512\n- RS384 \\*\n- HS256 \\*\n- HS384 \\*\n- RS256 \\*\n- PS384\n- ES256\n- ES512\n- EdDSA\n- HS512 \\*\n- PS256\n  **Note**: only the methods with `*` are supported for now.\n\n### Topic Configuration\n\n```yaml\ntype: \"\u003c\u003cName\u003e\u003e\"\ntemplate: \"\u003c\u003cregex template\u003e\u003e\"\nhash_type: 0|1\naccesses:\n  iss-0: \"\u003c\u003caccess\u003e\u003e\"\n  iss-1: \"\u003c\u003caccess\u003e\u003e\"\n```\n\n### Template\n\nTopic template is a string consist of [Variables](##Available_Variables) and [Functions](##Available_Functions)\nand regular expressions.\n\nVariables and Function are replaced first, and then the whole template will compile as a regular expression.\nThe end result will be compared against the requested topic.\n\n#### Example\n\nThis is template topic given in `vendor:topics[#]:template`.\n\n```yaml\n- type: driver_location\n  template: ^{{.company}}/driver/{{.sub}}/location$\n  accesses:\n    0: \"2\"\n    1: \"-1\"\n```\n\n```regex\n^{{.company}}/driver/{{HashID .hashType .sub (IssToSnappID .iss)}}/location/[a-zA-Z0-9-_]+$\n```\n\nAfter parsing the template we get something like this\n\n```regex\n// company=snapp\n// hashType=0\n// sub=D96ZbvJakLp4PYd\n// iss=0\n^snapp/driver/D96ZbvJakLp4PYd/location/[a-zA-Z0-9-_]+$\n```\n\nNow if the requested topic match the created topic it is considered as a valid topic for that particular user.\n\n```text\nrequested_topic: snapp/driver/D96ZbvJakLp4PYd/location/23fw49vxd\ncreated_topic_regex: ^snapp/driver/D96ZbvJakLp4PYd/location/[a-zA-Z0-9-_]+$\n```\n\n#### Available Variables\n\nThese are the variables available to use in the topic templates.\n\n- `iss`\n  issuer obtained from JWT token\n- `sub`\n  subject obtained from JWT token\n- `hashType`\n  Hash type field defined in topic template configuration\n\n  | HashType | Value |\n  | -------- | ----- |\n  | HashID   | 0     |\n  | MD5      | 1     |\n\n- `company`\n  company field defined in vendor configuration\n\n#### Available Functions\n\nThese are the function available to use in the topic templates.\n\n- `IssToEntity(iss string) string`\n  convert `iss` obtained from JWT token to defined entity in `issEntityMap`\n- `IssToPeer(iss string) string`\n  convert `iss` obtained from JWT token to define peer in `issPeerMap`\n- `IssToSnappID(iss string) string`\n  convert `iss` obtained from JWT token to `snappid.audience`\n- `HashID(hashType int, sub string, snappID snappid.audience)`\n  generated `hashID` for the given `subject` base on the `hashType` and `snappid.audience`\n\n**Note**: `snappid.audience` only is available for issuer 0 and 1 which are for driver and passenger respectively.\n\n#### Accesses\n\nList of all types of access on a topic.\n\n| Access              | Value |\n| ------------------- | ----- |\n| Subscribe           | 1     |\n| Publish             | 2     |\n| Subscribe \u0026 Publish | 3     |\n| None                | -1    |\n\n#### Suggested Issuers\n\nUse any value for issuer but if you have an entity called `Driver` or `Passenger`,\nwe recommend use the following issuers for them.\n\n| Issuer    | Value |\n| --------- | ----- |\n| Driver    | 0     |\n| Passenger | 1     |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnapp-incubator%2Fsoteria","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsnapp-incubator%2Fsoteria","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnapp-incubator%2Fsoteria/lists"}