{"id":50841048,"url":"https://github.com/tetherto/svc-facs-auth","last_synced_at":"2026-06-14T06:35:22.782Z","repository":{"id":354897636,"uuid":"812705411","full_name":"tetherto/svc-facs-auth","owner":"tetherto","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-13T14:49:03.000Z","size":134,"stargazers_count":0,"open_issues_count":3,"forks_count":3,"subscribers_count":8,"default_branch":"main","last_synced_at":"2026-05-13T15:25:34.480Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tetherto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-06-09T16:42:20.000Z","updated_at":"2026-05-13T13:22:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tetherto/svc-facs-auth","commit_stats":null,"previous_names":["tetherto/svc-facs-auth"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tetherto/svc-facs-auth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fsvc-facs-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fsvc-facs-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fsvc-facs-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fsvc-facs-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tetherto","download_url":"https://codeload.github.com/tetherto/svc-facs-auth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fsvc-facs-auth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34312072,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"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":[],"created_at":"2026-06-14T06:35:21.966Z","updated_at":"2026-06-14T06:35:22.777Z","avatar_url":"https://github.com/tetherto.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# svc-facs-auth\n \nThis is a facility to handle user authentivcaton that extends from `bfx-facs-base` to provide authentication management with support for generating and validating tokens, managing users, and handling permissions. It uses SQLite for storing user and token information and LRU for caching.\n\n## Configuration\n\nThis facility requires a config file in the following structure:\n\n```javascript\n{\n  \"a0\": {\n    \"superAdmin\": \"superadmin@localhost\", // Superadmin email address\n    \"ttl\": 5000, // Default token time-to-live in seconds\n    \"saltRounds\": 10, // Number of salt rounds for password hashing\n    \"trustProxy\": false, // See \"Client IP resolution\" below\n    \"roles\": { // Roles with associated permissions\n      \"admin\": [\n        \"miner:rw\",\n        \"container:rw\",\n        \"user:rw\"\n      ],\n      \"site_manager\": [\n        \"miner:rw\",\n        \"container:rw\",\n        \"user:r\"\n      ],\n      \"user\": [\"jobs:rw\"]\n    }\n  }\n}\n```\n\n### Client IP resolution\n\nTokens are bound to the IPs observed at issue time. By default the facility\ntrusts only the direct socket peer (`req.socket.remoteAddress`) and ignores\nthe spoofable `X-Forwarded-For` header — so callers behind a proxy will see\ntheir token bound to the proxy's address.\n\nIf your service runs behind a trusted reverse proxy, set `trustProxy: true`\n**and** configure your HTTP framework to validate the proxy chain itself\n(e.g. Express's `app.set('trust proxy', …)`). When enabled, the facility:\n- Checks for Cloudflare's `CF-Connecting-IP` header (if present and valid)\n- Trusts the framework-derived `req.ip` / `req.ips` (from `X-Forwarded-For`)\n- Always includes `req.socket.remoteAddress` as a fallback\n\nThe raw `X-Forwarded-For` header is never consumed directly for security reasons.\n\n### Roles\n\n`createUser` and `updateUser` only accept role names declared as keys in\n`conf.roles`. The super-admin marker (`*`) cannot be assigned via these\nAPIs — it is reserved for the bootstrap super-admin written by `_initDb`.\n`updateUser` additionally requires the caller's token to hold `user:rw`\nwhenever the requested roles differ from the user's current roles.\n\n## Documentation\n### `auth.createUser(req)`\nCreates a new user with specified roles and permissions.\n\n**Parameters:**\n- `req\u003cobject\u003e`: Object with user creation details.\n    - `email\u003cstring\u003e`: Email address of the user.\n    - `roles\u003cstring[]\u003e`: Array of roles for the user.\n    - `password\u003cstring\u003e`: Password for the user.\n\n```javascript\nconst result = await auth.createUser({\n  email: 'user@example.com',\n  roles: ['admin']\n})\n```\n\n### `auth.updateUser(req)`\nUpdates an existing user with new roles and permissions.\n\n**Parameters:**\n- `req\u003cobject\u003e`: Object with user update details.\n    - `token\u003cstring\u003e`: Authentication token for the user.\n    - `email\u003cstring\u003e`: Email address of the user.\n    - `roles\u003cstring[]\u003e`: Array of roles for the user.\n    - `password\u003cstring\u003e`: New password for the user.\n\n```javascript\nconst result = await auth.updateUser({\n  token: 'some-token',\n  email: 'new@example.com',\n  roles: ['admin']\n})\n```\n\n### `auth.compareUser(req)`\nCompares user details (email, password, roles) with the stored user information.\n\n**Parameters:**\n- `req\u003cobject\u003e`: Object containing user details for comparison.\n    - `token\u003cstring\u003e`: Authentication token for the user.\n    - `email\u003cstring\u003e (optional)`: Email address to compare.\n    - `roles\u003cstring[]\u003e (optional)`: Array of roles to compare.\n    - `password\u003cstring\u003e (optional)`: Password to compare.\n\n```javascript\nconst isMatching = await auth.compareUser({\n  token: 'user-token',\n  email: 'test@example.com',\n  roles: ['user'],\n  password: 'securepassword'\n})\nconsole.log(isMatching) // true or false\n```\n\n### `auth.genToken(req)`\nGenerates a new authentication token based on the provided parameters. It validates the input, allocates resources, and stores the token data.\n\n**Parameters:**\n- `req\u003cobject\u003e`: Object containing token generation details.\n    - `ips\u003cstring[]\u003e`: List of IP addresses associated with the token.\n    - `userId\u003cnumber\u003e`: User ID for whom the token is generated.\n    - `ttl\u003cnumber\u003e`: Time-to-live for the token in seconds (default: 300).\n    - `metadata\u003cobject\u003e`: Optional metadata associated with the token.\n    - `pfx\u003cstring\u003e`: Prefix for the token (default: 'pub').\n    - `scope\u003cstring\u003e`: Scope for the token (default: 'api').\n    - `roles\u003cstring[]\u003e`: Array of roles for the token.\n\n```javascript\nconst token = await auth.genToken({\n  ips: ['192.168.1.1'],\n  userId: 1,\n  ttl: 3600,\n  metadata: { key: 'value' },\n  pfx: 'pub',\n  scope: 'api',\n  roles: ['admin']\n})\n```\n\n### `auth.regenerateToken(req)`\nRegenerates an existing authentication token. It validates the old token, checks permissions, and creates a new token.\n\n**Parameters:**\n- `req\u003cobject\u003e`: Object with token regeneration details.\n    - `oldToken\u003cstring\u003e`: Existing token to be regenerated.\n    - `ips\u003cstring[]\u003e`: New IP addresses associated with the token (optional).\n    - `ttl\u003cnumber\u003e`: Time-to-live for the new token in seconds (default: 300).\n    - `pfx\u003cstring\u003e`: Prefix for the new token (default: 'pub').\n    - `scope\u003cstring\u003e`: Scope for the new token (default: 'api').\n    - `roles\u003cstring[]\u003e`: Array of roles for the new token.\n\n```javascript\nconst newToken = await auth.regenerateToken({\n  oldToken: 'existing-token',\n  ips: ['192.168.1.1'],\n  ttl: 3600,\n  pfx: 'pub',\n  scope: 'api',\n  roles: ['admin']\n})\n```\n\n### `auth.getTokenPerms(token)`\nRetrieves permissions associated with a token.\n\n**Parameters:**\n- `token\u003cstring\u003e`: The token to get permissions for.\n\n**Returns:**\n- `{ superadmin: \u003cboolean\u003e, perms: \u003cstring[]\u003e }` with token permissions.\n\n```javascript\nconst perms = auth.getTokenPerms('some-token')\nconsole.log('Token permissions:', perms)\n```\n\n### `auth.resolveToken(token, ips)`\nValidates a token and checks if it is associated with the given IP addresses.\n\n**Parameters:**\n- `token\u003cstring\u003e`: The token to resolve.\n- `ips\u003cstring[]\u003e`: List of IP addresses to validate.\n\n```javascript\nconst user = await auth.resolveToken('some-token', ['192.168.1.1'])\n```\nNote: User data (`user`) is coming from the `auth_token` table.\n\n### `auth.tokenHasPerms(token, perm)`\nChecks if a token has the required permissions.\n\n**Parameters:**\n- `token\u003cstring\u003e`: The token to check.\n- `perm\u003cstring\u003e`: Permission to check.\n\n**Returns:**\n- `true` if the token has the required permissions.\n- `false` otherwise.\n\n```javascript\nconst hasPerms = auth.tokenHasPerms('some-token', 'miner:r')\nconsole.log('Token has required permissions:', hasPerms)\n```\n\n### `auth.cleanupTokens()`\nCleans up expired tokens from the database.\n\n```javascript\nawait auth.cleanupTokens()\n```\n\n### `auth.addHandlers(handlers)`\nAdds authentication handlers to the service.\n\n**Parameters:**\n- `handlers\u003cobject\u003e`: Object containing authentication handlers, each key is a handler name and value is a handler function.\n\n```javascript\nauth.addHandlers({\n  'handler-name': async (ctx, req) =\u003e {\n    // Handler logic\n  }\n})\n```\n\n### `auth.authCallbackHandler(type, req)`\nHandles authentication callbacks by resolving tokens and returning authentication results.\n\n**Parameters:**\n- `type\u003cstring\u003e`: Type of authentication callback.\n- `req\u003cobject\u003e`: Request object containing callback details.\n\n```javascript\nconst token = await auth.authCallbackHandler('callback-type', request)\n```\n\n### `auth.getUserById(id)`\nReturn the user with the given id\n\n**Parameters:**\n- `id\u003cstring\u003e`: id of the user.\n\n```javascript\nconst user = await auth.getUserById('3')\n```\n\n### `auth.getUserByEmail(email)`\nReturn the user with the given email\n\n**Parameters:**\n- `id\u003cstring\u003e`: email of the user.\n\n```javascript\nconst user = await auth.getUserByEmail('new@example.com')\n```\n\n### `auth.listUsers()`\nReturns a list of users present\n\n```javascript\nconst users = await auth.listUsers('callback-type', request)\n```\n\n### `auth.deleteUser(id)`\nDeletes the user with the provided id.\n\n**Parameters:**\n- `id\u003cstring\u003e`: id of the user.\n\n```javascript\nawait auth.deleteUser('23')\n```\n\n### `auth.mfaHandler(type, req)`\nHandles multi-factor authentication (MFA) by invoking the specified MFA handler.\n\n**Parameters:**\n- `type\u003cstring\u003e`: The type/name of the MFA handler to invoke (e.g., `'totp'`).\n- `req\u003cobject\u003e`: The request object containing necessary authentication details.\n\n**Throws:**\n- `ERR_HANDLER_INVALID` if the specified handler does not exist or is not a function.\n\n```javascript\nconst result = await auth.mfaHandler('totp', { totp: '123456' , ...\u003cObject\u003e })\n```\n\n### `auth.mfaCallbackHandler(type, req, getUserMfaMethods)`\nHandles authentication callbacks with MFA support. If MFA is required, returns a CSRF token and the list of required MFA methods; otherwise, returns the authentication token.\n\n**Parameters:**\n- `type\u003cstring\u003e`: The type of authentication callback.\n- `req\u003cobject\u003e`: The request object containing callback details.\n- `getUserMfaMethods\u003cfunction\u003e`: A function that returns the list of enabled MFA methods for the user.\n\n**Returns:**\n- If MFA is required:\n  An object containing `csrf_token`, `mfa_required: true`, and `mfa_methods` (array of enabled MFA methods).\n- If MFA is not required:\n  An object containing the authentication `token`.\n\n**Throws:**\n- `ERR_MFA_METHOD_HANDLER_INVALID` if getUserMfaMethods is not a function.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftetherto%2Fsvc-facs-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftetherto%2Fsvc-facs-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftetherto%2Fsvc-facs-auth/lists"}