{"id":25303598,"url":"https://github.com/daedaluz/uyulala","last_synced_at":"2025-04-07T02:32:18.757Z","repository":{"id":222447552,"uuid":"756859474","full_name":"Daedaluz/uyulala","owner":"Daedaluz","description":"Web based authenticator, implementing OAuth2 code flow and BankID-like api interface.","archived":false,"fork":false,"pushed_at":"2024-12-27T13:16:00.000Z","size":3914,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-13T07:37:39.913Z","etag":null,"topics":["extended-ciba","oauth2","oidc","passkeys","webauthn"],"latest_commit_sha":null,"homepage":"","language":"Go","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/Daedaluz.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":"2024-02-13T13:00:07.000Z","updated_at":"2024-12-27T13:16:04.000Z","dependencies_parsed_at":"2024-02-14T09:43:58.431Z","dependency_job_id":"1b899751-9c09-4bfe-a88e-378c040ffc2b","html_url":"https://github.com/Daedaluz/uyulala","commit_stats":null,"previous_names":["daedaluz/uyulala"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daedaluz%2Fuyulala","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daedaluz%2Fuyulala/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daedaluz%2Fuyulala/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Daedaluz%2Fuyulala/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Daedaluz","download_url":"https://codeload.github.com/Daedaluz/uyulala/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247581648,"owners_count":20961811,"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":["extended-ciba","oauth2","oidc","passkeys","webauthn"],"created_at":"2025-02-13T07:37:28.189Z","updated_at":"2025-04-07T02:32:18.702Z","avatar_url":"https://github.com/Daedaluz.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Uyulala\n\n## Description\n\nUyulala at it's core is a web based authenticator that only uses `passkeys` as a means of authentication.\nImplementing a Bank-ID similar api and a OAuth2 interface, enables uyulala to be used as a IDP for other applications.\nIt is a simple and secure way to authenticate users without the need for usernames or passwords.\n\n## Features\n\n- [x] Create passkeys\n- [x] Create apps\n- [x] Authenticate users\n- [x] OAuth2 Provider with PKCE support\n- [x] Bank-ID similar api\n- [x] Consider move the front-end to its own repo (update: It will stay in-repo for simplicity)\n- [x] Create a suitable Cross-Origin policy\n- [x] Actually create challenges that are hash-related to the sing-data, allowing \"Document signing\" (only BankID flow)\n- [x] Consider adding fictional email to ID Tokens based on user-id eg `ABCDEFG@uyulala.local`\n\n## Future plans\n\n- [ ] Better error handling, logging and documentation\n- [ ] Nicer Web UI\n- [ ] Look over any potential useless / missing data with the response from the collect api; it should contain\n  everything to validate the signature\n- [ ] Should admin apps be able to create users with arbitrary user-ids? (easier to integrate with other solutions?)\n- [ ] Make up some configuration / rule system for accepting new keys (eg only allow keys with a certain certification\n  level)\n- [ ] ....\n\n## Scrapped\n- [x] Replace the websocket-based remote-signer with some webrtc-based solution (eliminate load-balancer issue with\n  multiple instances)?\n\n\n## Running a local test server\n\n```bash\n./scripts/docker-compose.sh up\n```\n\nTo remove:\n\n```bash \n./scripts/docker-compose.sh down\n```\n\nPoint a browser at `https://localhost:8080/demo`\n\n## Test use case\n\n1) Protect a grafana instance with uyulala via custom oauth2 settings.\n    ```bash\n    PROFILE=use-case ./scripts/docker-compose.sh up\n    ```\n   To remove:\n    ```bash\n    PROFILE=use-case ./scripts/docker-compose.sh down\n    ```\n2) Create a user with a registered key by going to `https://localhost/demo`\n3) point browser to `http://localhost:3000/`\n4) Authenticate with key\n5) ????\n6) Profit\n\n## BankID flow\n\n```mermaid\nsequenceDiagram\n    ClientFrontend -\u003e\u003e ClientServer: Login\n    ClientServer -\u003e\u003e Uyulala: /api/v1/sign\n    Uyulala -\u003e\u003e ClientServer: {\"challengeId\":\"xxx\"}\n    ClientServer -\u003e\u003e ClientFrontend: Present Link / Qr\n    ClientServer --\u003e\u003e Uyulala: /api/v1/collect\n    Note over ClientServer, Uyulala: Repeat till success or rejected/expired\n    User -\u003e\u003e Uyulala: Signs / Reject\n    ClientServer -\u003e\u003e Uyulala: /api/v1/collect\n    Uyulala -\u003e\u003e ClientServer: {\"status\": \"success\", ...}\n    ClientServer -\u003e\u003e ClientFrontend: User Loged in\n```\n\n## Challenge hash calculation\n\nWhen challenges are created with a `text` prompt, the webauthn challenge is calculated as follows:\n\nSHA256(UserID + '\\n' + AppID + '\\n' + ChallengeID '\\n' + nonce + '\\n' + Text + '\\n' + Data)\n\n## API\n\nThe API is split into four parts;\n\n* Client API - Used by applications, protected by client id and client secret (create signature /collect challenges etc)\n* Service API - Used by applications with administrative flag set, protected by client id and client secret. Can create\n  users and register new keys to users.\n* User API [wip] - Used by the user, protected by some JWT issuer. User self-service for managing keys.\n* Public API - Used by the web ui and has no authentication (Sign a challenge / get challenge or create an oauth2\n  challenge)\n\n---\n\n### Client API\n\nClient authentication is done with Basic authentication using client id as username and client secret as password.\nIf OAuth2 is used, client id and client secret can be sent both as form data and basic auth.\n\n---\nPOST `/api/v1/collect`\n\n```bash\ncurl -u \"demo:demo\" \\\n     -H 'Content-Type: application/json' \\\n     -d '{\"challengeId\":\"challenge-id\"}' \\\n     http://localhost:8080/api/v1/collect\n```\n\nThis endpoint is similar to the Bank-ID collect in the same sense that it is used to collect a challenge and that the\nclient should poll this endpoint until it either expires or the user signs the challenge.\nThis endpoint also doubles as the OAuth2 token endpoint if the `Content-Type` header\nis `application/x-www-form-urlencoded`.\n\nUsing OAuth2, the code is generated when the user has signed the challenge and the challenge is started with an oauth2\nflow.\n\n* `challengeId` - The challenge id to collect\n\nExample request payload\n\n```json\n{\n  \"challengeId\": \"12ca6a2e-f783-4545-92f2-4d80cb74de45\"\n}\n```\n\nExample signed result:\n\n```json\n{\n  \"challengeId\": \"12ca6a2e-f783-4545-92f2-4d80cb74de45\",\n  \"userId\": \"ABCDEFG\",\n  \"status\": \"signed\",\n  \"signed\": \"1970-01-01T00:00:00Z\",\n  \"userPresent\": true,\n  \"userVerified\": true,\n  \"publicKey\": \"\",\n  \"assertionResponse\": {\n    \"clientDataJSON\": \"\",\n    \"authenticatorData\": \"\",\n    \"signature\": \"abasc\",\n    \"userHandle\": \"abcdef\"\n  },\n  \"challenge\": \"\",\n  \"signatureData\": {\n    \"text\": \"\",\n    \"data\": \"\"\n  }\n}\n```\n\nExample pending result (Not yet viewed):\n\n```json\n{\n  \"msg\": \"Challenge has not been signed yet\",\n  \"status\": \"pending\"\n}\n```\n\nExample viewed result:\n\n```json\n{\n  \"msg\": \"Challenge has not been signed yet\",\n  \"status\": \"viewed\"\n}\n```\n\nExample rejected result:\n\n```json\n{\n  \"msg\": \"Challenge has been rejected\",\n  \"status\": \"rejected\"\n}\n```\n\nExample collected result\n\n```json\n{\n  \"msg\": \"Challenge has already been collected\",\n  \"status\": \"collected\"\n}\n```\n\nExample expired result:\n\n```json\n{\n  \"msg\": \"Challenge has expired\",\n  \"status\": \"expired\"\n}\n```\n\n---\nPOST `/api/v1/sign`\n\nThis api is used to create a challenge for the user to sign, also inspired by the Bank-ID api.\nThe application needs to redirect the user to the authenticator page with the challenge id as a query parameter\neg `http://localhost:8080/authenticator?challengeId=12ca6a2e-f783-4545-92f2-4d80cb74de45`\n\n* `userId` - The user id to sign the challenge for. This can be empty thus creating a challenge anyone with a\n  key can sign (think Login). When called with a user id, only the user with that id can sign the challenge.\n* `userVerification` - The user verification level required to sign the\n  challenge (`required`, `preferred`, `discouraged`)\n  The exact implementation of this is up to the authenticator used, but usually some biometric or pin is involved.\n* `text` - The text to sign\n* `data` - Base64 encoded data to sign (If data is provided, text must be provided)\n* `timeout` - The time in seconds before the challenge expires\n* `redirect` - The redirect url to send the user to after signing the challenge.\n  Must be an url that is registered to the app that created the challenge.\n\nExample request payload:\n\n```json\n{\n  \"userId\": \"ABCDEFG\",\n  \"userVerification\": \"required\",\n  \"text\": \"\",\n  \"data\": \"\",\n  \"timeout\": 300,\n  \"redirect\": \"https://example.com/authenticated\"\n}\n```\n\nExample response payload:\n\n```json\n{\n  \"challenge_id\": \"cbe4748d-2c98-434f-8e72-d32fbbdc86b8\"\n}\n```\n\n---\n\n### Public API\n\nThe public api is used by the web ui and has no authentication.\n\n---\n\nGET `/api/v1/challenge/:id`\n\nThis api is used to get the challenge data for a specific challenge id.\n\n```bash\ncurl -H 'Content-Type: application/json' \\\n     http://localhost:8080/api/v1/challenge/12ca6a2e-f783-4545-92f2-4d80cb74de45\n```\n\nexample response payload for signing:\n\n```json\n{\n  \"app\": {\n    \"id\": \"nfh17afcbd1e6add1d1d\",\n    \"name\": \"demo\",\n    \"created\": \"2023-11-15T08:25:56Z\",\n    \"description\": \"\",\n    \"icon\": \"\",\n    \"idTokenAlg\": \"RS256\",\n    \"keyId\": \"asdavafafadqd\",\n    \"admin\": false\n  },\n  \"expire\": 1700146828,\n  \"publicKey\": {\n    \"challenge\": \"\u003csome challenge\u003e\",\n    \"timeout\": 300000,\n    \"rpId\": \"localhost\",\n    \"allowCredentials\": [\n      {\n        \"type\": \"public-key\",\n        \"id\": \"\"\n      },\n      {\n        \"type\": \"public-key\",\n        \"id\": \"\"\n      }\n    ],\n    \"userVerification\": \"required\"\n  },\n  \"type\": \"webauthn.get\"\n}\n```\n\nexample response payload for creating a key:\n\n```json\n{\n  \"app\": {\n    \"id\": \"nfh17afcbd1e6add1d1d\",\n    \"name\": \"demo\",\n    \"created\": \"2023-11-15T08:25:56Z\",\n    \"description\": \"\",\n    \"icon\": \"\",\n    \"idTokenAlg\": \"RS256\",\n    \"keyId\": \"aaaadbbasdasd\",\n    \"admin\": true\n  },\n  \"expire\": 1700087418,\n  \"publicKey\": {\n    \"rp\": {\n      \"name\": \"uyulala\",\n      \"id\": \"localhost\"\n    },\n    \"user\": {\n      \"name\": \"Kalle Anka\",\n      \"displayName\": \"Kalle Anka\",\n      \"id\": \"ABCDEFG\"\n    },\n    \"challenge\": \"\",\n    \"pubKeyCredParams\": [\n      {\n        \"type\": \"public-key\",\n        \"alg\": -7\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -35\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -36\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -257\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -258\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -259\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -37\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -38\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -39\n      },\n      {\n        \"type\": \"public-key\",\n        \"alg\": -8\n      }\n    ],\n    \"timeout\": 300000,\n    \"authenticatorSelection\": {\n      \"authenticatorAttachment\": \"cross-platform\",\n      \"requireResidentKey\": true,\n      \"residentKey\": \"required\",\n      \"userVerification\": \"required\"\n    },\n    \"attestation\": \"direct\"\n  },\n  \"type\": \"webauthn.create\"\n}\n```\n\n---\n\nPOST `/api/v1/challenge/:id`\n\nThis api is used to sign a challenge.\n\nexample post data:\n\n```json\n{\n  \"response\": {\n    \"clientDataJSON\": \"...\",\n    \"authenticatorData\": \"...\",\n    \"signature\": \"...\",\n    \"userHandle\": \"...\"\n  },\n  \"rawId\": \"...\",\n  \"authenticatorAttachment\": \"cross-platform\",\n  \"type\": \"public-key\",\n  \"id\": \"....\"\n}\n```\n\nexample response payload:\n\n```json\n{\n  \"redirect\": \"http://localhost:8080/demo?challengeId=12ca6a2e-f783-4545-92f2-4d80cb74de45\"\n}\n```\n\n---\n\n### Service API\n\nThe service api is used to create / delete users and add / remove keys respectively.\nService authentication is same as with the client api, but the client needs the `admin` flag set during creation.\n\n---\nGET `/api/v1/service/list/users`\n\n```bash\ncurl -u \"demo:demo\" \\\n     -H 'Content-Type: application/json' \\\n     http://localhost:8080/api/v1/service/list/users\n```\n\nexample response payload:\n\n```json\n[\n  {\n    \"id\": \"ea85972bed2a603fb4480ff6980fd530a846\",\n    \"created\": \"2023-11-15T08:31:02Z\",\n    \"keys\": [\n      {\n        \"hash\": \"\u003csha hash of key\u003e\",\n        \"key\": {\n          \"ID\": \"\u003ckey id\u003e\",\n          \"PublicKey\": \"\u003cpublic key\u003e\",\n          \"AttestationType\": \"packed\",\n          \"Transport\": null,\n          \"Flags\": {\n            \"UserPresent\": true,\n            \"UserVerified\": true,\n            \"BackupEligible\": false,\n            \"BackupState\": false\n          },\n          \"Authenticator\": {\n            \"AAGUID\": \"\u003csome AAGUID\u003e\",\n            \"SignCount\": 14,\n            \"CloneWarning\": false,\n            \"Attachment\": \"cross-platform\"\n          }\n        },\n        \"created\": \"2023-11-15T08:31:11Z\",\n        \"lastUsed\": \"2023-11-15T21:31:09Z\"\n      },\n      {\n        \"hash\": \"\u003csha hash of key\u003e\",\n        \"key\": {\n          \"ID\": \"\",\n          \"PublicKey\": \"\",\n          \"AttestationType\": \"none\",\n          \"Transport\": null,\n          \"Flags\": {\n            \"UserPresent\": true,\n            \"UserVerified\": true,\n            \"BackupEligible\": true,\n            \"BackupState\": true\n          },\n          \"Authenticator\": {\n            \"AAGUID\": \"\",\n            \"SignCount\": 0,\n            \"CloneWarning\": false,\n            \"Attachment\": \"cross-platform\"\n          }\n        },\n        \"created\": \"2023-11-15T09:44:09Z\",\n        \"lastUsed\": \"2023-11-15T09:47:11Z\"\n      }\n    ]\n  }\n]\n```\n\n---\nPOST `/api/v1/service/create/user`\n\nThis api creates a new user and returns a new challenge id that creates the users first key when signed.\nLike the sign api, the application needs to redirect the user to the authenticator page with the challenge id as a query\nparameter.\n\n```bash\ncurl -u \"demo:demo\" \\\n     -H 'Content-Type: application/json' \\\n     -d '{\"suggestedName\": \"Kalle Anka\", \"timeout\": 380, \"redirect\": \"http://localhost:8080/demo\"}' \\\n     http://localhost:8080/api/v1/service/create/user\n```\n\nexample request payload:\n\n```json\n{\n  \"suggestedName\": \"Kalle Anka\",\n  \"timeout\": 380,\n  \"redirect\": \"http://localhost:8080/demo\"\n}\n```\n\nexample response payload:\n\n```json\n{\n  \"challengeId\": \"12ca6a2e-f783-4545-92f2-4d80cb74de45\"\n}\n```\n\n---\nPOST `/api/v1/service/create/key`\n\nThis api creates a new key for the user and returns a new challenge id that creates the key associated with the user\nwhen signed.\nLike the sign api, the application needs to redirect the user to the authenticator page with the challenge id as a query\nparameter.\n\n```bash\ncurl -u \"demo:demo\" \\\n     -H 'Content-Type: application/json' \\\n     -d '{\"userId\": \"ABCDEFG\", \"timeout\": 380, \"redirect\": \"http://localhost:8080/demo\", \"suggestedName\": \"Kalle Anka\"}' \\\n     http://localhost:8080/api/v1/service/create/key\n```\n\nexample request payload:\n\n```json\n{\n  \"userId\": \"ABCDEFG\",\n  \"timeout\": 380,\n  \"redirect\": \"http://localhost:8080/demo\",\n  \"suggestedName\": \"Kalle Anka\"\n}\n```\n\nexample response payload:\n\n```json\n{\n  \"challengeId\": \"12ca6a2e-f783-4545-92f2-4d80cb74de45\"\n}\n```\n\n---\n\nPOST `/api/v1/service/delete/key`\n\nThis api deletes a key for the user.\n\n```bash \ncurl -u \"demo:demo\" \\\n     -H 'Content-Type: application/json' \\\n     -d '{\"userId\": \"ABCDEFG\", \"keyHash\": \"\u003ckey id sha hash\u003e\"}' \\\n     http://localhost:8080/api/v1/service/delete/key\n```\n\nexample request payload:\n\n```json\n{\n  \"userId\": \"ABCDEFG\",\n  \"keyHash\": \"\u003ckey id sha hash\u003e\"\n}\n```\n\nexample response payload:\n\n```json\n{\n  \"status\": \"deleted\"\n}\n```\n\n---\n\nPOST `/api/v1/service/delete/user`\n\nThis api deletes a user and all associated keys.\n\n```bash\ncurl -u \"demo:demo\" \\\n     -H 'Content-Type: application/json' \\\n     -d '{\"userId\": \"ABCDEFG\"}' \\\n     http://localhost:8080/api/v1/service/delete/user\n```\n\nexample request payload:\n\n```json\n{\n  \"userId\": \"ABCDEFG\"\n}\n```\n\nexample response payload:\n\n```json\n{\n  \"status\": \"deleted\"\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaedaluz%2Fuyulala","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaedaluz%2Fuyulala","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaedaluz%2Fuyulala/lists"}