[Feat req] Service Accounts #1445

Open
opened 2026-03-13 08:40:33 -05:00 by GiteaMirror · 12 comments
Owner

Originally created by @maelp on GitHub (Jun 30, 2025).

Is this suited for github?

  • Yes, this is suited for github

It would be nice, in addition to "user tokens", to have a notion of "Service Accounts" that would be added to an organization, without being linked to a particular user, so that if the user is no longer part of the organization, the service account is still functional and linked to the organization.

What would be the best way to do this with existing BetterAuth features ? and if it's not possible, would it be feasible to add this feature?

Each service account could have roles/permissions/etc

Describe the solution you'd like

A way for a user to create Service Accounts that have a given role/permission set, so that he can do machine-to-machine calls for APIs without logging with username/password, and those ServiceAccounts would be linked "to the organization" rather than "to the user", so that if the user leaves the organization, the organization can still run the scripts / etc which relied on those service accounts

Describe alternatives you've considered

Not sure?

Additional context

No response

Originally created by @maelp on GitHub (Jun 30, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. It would be nice, in addition to "user tokens", to have a notion of "Service Accounts" that would be added to an organization, without being linked to a particular user, so that if the user is no longer part of the organization, the service account is still functional and linked to the organization. What would be the best way to do this with existing BetterAuth features ? and if it's not possible, would it be feasible to add this feature? Each service account could have roles/permissions/etc ### Describe the solution you'd like A way for a user to create Service Accounts that have a given role/permission set, so that he can do machine-to-machine calls for APIs without logging with username/password, and those ServiceAccounts would be linked "to the organization" rather than "to the user", so that if the user leaves the organization, the organization can still run the scripts / etc which relied on those service accounts ### Describe alternatives you've considered Not sure? ### Additional context _No response_
GiteaMirror added the enhancement label 2026-03-13 08:40:33 -05:00
Author
Owner

@bnssoftware commented on GitHub (Jul 14, 2025):

This would be a very useful feature, especially when used with api keys. Currently, an api key is attached to a user. This won't work in a common enterprise use case where an api key is given to a third party to be able to make api calls to some system that would not be tied to a user, but rather, a non expiring service account that itself has certain permissions. We could probably hack our way around this by creating a user disguised as a service account, but would be much better if this was built in.

@bnssoftware commented on GitHub (Jul 14, 2025): This would be a very useful feature, especially when used with api keys. Currently, an api key is attached to a user. This won't work in a common enterprise use case where an api key is given to a third party to be able to make api calls to some system that would not be tied to a user, but rather, a non expiring service account that itself has certain permissions. We could probably hack our way around this by creating a user disguised as a service account, but would be much better if this was built in.
Author
Owner

@maelp commented on GitHub (Jul 14, 2025):

Agreed!

@maelp commented on GitHub (Jul 14, 2025): Agreed!
Author
Owner

@brianwk commented on GitHub (Aug 10, 2025):

+1

@brianwk commented on GitHub (Aug 10, 2025): +1
Author
Owner

@XavierGeerinck commented on GitHub (Aug 16, 2025):

+1

@XavierGeerinck commented on GitHub (Aug 16, 2025): +1
Author
Owner

@OscarMayReal commented on GitHub (Aug 19, 2025):

+1

@OscarMayReal commented on GitHub (Aug 19, 2025): +1
Author
Owner

@maelp commented on GitHub (Aug 20, 2025):

A lot of people seem to be interested! That's great!

Perhaps among the ones who replied to the thread, there are people with enough background on how to design an effective service account system who could help draft a proposal of what it should look like?

@maelp commented on GitHub (Aug 20, 2025): A lot of people seem to be interested! That's great! Perhaps among the ones who replied to the thread, there are people with enough background on how to design an effective service account system who could help draft a proposal of what it should look like?
Author
Owner

@maelp commented on GitHub (Aug 20, 2025):

As a base reference, here is a rough draft designed with discussing a few minutes with ChatGPT:

What “Service Accounts” are

Definition: Non-human principals used by automation or backend services.
Anchors: Each Service Account (SA) is owned by either:

  • an Organization (org-scoped SA), or
  • a User (user-scoped SA; useful for personal projects / solo developers).

Minimal data model (plugin tables)

service_accounts:
id (sa_*) | owner_type ('org'|'user') | owner_id | name | status | attrs(jsonb) | created_at

service_account_keys (OAuth/JWT or simple bearer):
id | sa_id | kind('oauth-jwk'|'simple') | public_jwk | secret_hash | kid | created_at | expires_at | last_used_at

service_account_oidc_trusts (optional federation):
id | sa_id | issuer | subject_constraint | audience | claims(jsonb) | created_at (for mapping external OIDC identities → SA)

👉 Reuse BetterAuth’s existing org/user tables; no changes required

Authentication options

  1. BetterAuth-issued OAuth2 (client_credentials) / JWT
    • SA has 1..N active keypairs (JWK).
    • Token endpoint: /oauth/token (grant_type=client_credentials) → short-lived JWT (5–30 min).
    • Why: standards-based m2m; good default.
  2. BetterAuth OIDC Provider (Workload Federation) — optional
    • Use BetterAuth’s OIDC Provider plugin to exchange an external workload OIDC token (e.g., GitHub Actions) for an SA token via a simple STS-like /token/oidc/exchange.
    • Trusts are pinned by issuer, sub/repo, aud, etc.
    • Why: avoids long-lived secrets; ideal for CI/CD.
  3. Simple bearer tokens (legacy / “easy mode”) — optional
    • Per-SA opaque token (stored hashed), short TTL, scoped.
    • Why: dead-simple deployments; mark as legacy path.

Note: BetterAuth already supports running as an IDP via its OIDC provider plugin; this proposal extends it for service-to-service use and adds SA ownership + scoping.

Token shape (JWT)

Signed by BetterAuth (EdDSA/ES256). Example claims:

iss: https://auth.yourapp.example
sub: sa_123…            # the service account
aud: api.yourapp.example
exp/iat: 
owner: { type: "org", id: "org_abc…" }  # or { type: "user", id: "usr_…" }
roles: ["project.deployer"]             # optional (RBAC bundle)
scope: "resource.read resource.write"   # optional (OAuth scopes)
attrs: { env: "prod", team: "data" }    # optional (ABAC)
act: { impersonator: "usr_…", reason: "maintenance" } # if impersonation used
jti: 

Authorization model

  • Scopes (flat, match API surface): e.g., resource.read, resource.write.
  • Roles (bundles of scopes): e.g., project.deployer → resource.read, deploy.run.
  • Bindings: (principal=SA|User|OrgGroup, role|scope, target_resource), with inheritance optional later.
  • ABAC (optional): evaluate simple attribute checks (e.g., token.attrs.env == request.env).

This gives BetterAuth a lightweight RBAC today, with room for ABAC later.

Admin & lifecycle

  • Create SA: choose owner (org/user), name, tags.
  • Add credentials:
    • Add keypair (OAuth/JWT) → returns client_id, kid, private key (show once).
    • Add OIDC trust → set issuer, subject/claims constraints.
    • Create simple token (optional) → show once.
  • Bind permissions: attach roles/scopes to SA (optionally at a resource path).
  • Rotate keys: allow overlapping validity; show last_used_at to guide cleanup.
  • Disable / delete with soft-delete window.
  • Audit: every token mint/introspect and every authz decision emits an event (actor, target, outcome, token.jti).

Security defaults

  • Short token TTL (10–30 min) + jti tracking (revocation list for high-risk).
  • Multiple active keys per SA; last-used timestamps; rotate ≤90 days.
  • OIDC trusts must pin issuer + at least one constrained claim (e.g., sub or repository).
  • Hash opaque tokens; show once; rate-limit per SA.

How this fits BetterAuth today

  • Concepts: Users & Organizations already exist; SAs simply add a non-human principal anchored to these.
  • OIDC: Reuse the OIDC Provider plugin to support federation/IDP use and add one STS-like exchange endpoint—no new external infra.
  • Extensibility: Implement as a BetterAuth plugin adding:
    • new tables (above),
    • token mint/exchange handlers,
    • lightweight RBAC bindings,
    • admin helpers (create/rotate/disable).
@maelp commented on GitHub (Aug 20, 2025): As a base reference, here is a rough draft designed with discussing a few minutes with ChatGPT: ## What “Service Accounts” are Definition: Non-human principals used by automation or backend services. Anchors: Each Service Account (SA) is owned by either: - an Organization (org-scoped SA), or - a User (user-scoped SA; useful for personal projects / solo developers). ## Minimal data model (plugin tables) service_accounts: `id (sa_*) | owner_type ('org'|'user') | owner_id | name | status | attrs(jsonb) | created_at` service_account_keys (OAuth/JWT or simple bearer): `id | sa_id | kind('oauth-jwk'|'simple') | public_jwk | secret_hash | kid | created_at | expires_at | last_used_at` service_account_oidc_trusts (optional federation): `id | sa_id | issuer | subject_constraint | audience | claims(jsonb) | created_at (for mapping external OIDC identities → SA)` 👉 Reuse BetterAuth’s existing org/user tables; no changes required ## Authentication options 1. BetterAuth-issued OAuth2 (client_credentials) / JWT - SA has 1..N active keypairs (JWK). - Token endpoint: /oauth/token (grant_type=client_credentials) → short-lived JWT (5–30 min). - Why: standards-based m2m; good default. 2. BetterAuth OIDC Provider (Workload Federation) — optional - Use BetterAuth’s OIDC Provider plugin to exchange an external workload OIDC token (e.g., GitHub Actions) for an SA token via a simple STS-like /token/oidc/exchange. - Trusts are pinned by issuer, sub/repo, aud, etc. - Why: avoids long-lived secrets; ideal for CI/CD. 3. Simple bearer tokens (legacy / “easy mode”) — optional - Per-SA opaque token (stored hashed), short TTL, scoped. - Why: dead-simple deployments; mark as legacy path. Note: BetterAuth already supports running as an IDP via its OIDC provider plugin; this proposal extends it for service-to-service use and adds SA ownership + scoping. ## Token shape (JWT) Signed by BetterAuth (EdDSA/ES256). Example claims: ```yaml iss: https://auth.yourapp.example sub: sa_123… # the service account aud: api.yourapp.example exp/iat: … owner: { type: "org", id: "org_abc…" } # or { type: "user", id: "usr_…" } roles: ["project.deployer"] # optional (RBAC bundle) scope: "resource.read resource.write" # optional (OAuth scopes) attrs: { env: "prod", team: "data" } # optional (ABAC) act: { impersonator: "usr_…", reason: "maintenance" } # if impersonation used jti: … ``` ## Authorization model - Scopes (flat, match API surface): e.g., resource.read, resource.write. - Roles (bundles of scopes): e.g., project.deployer → resource.read, deploy.run. - Bindings: (principal=SA|User|OrgGroup, role|scope, target_resource), with inheritance optional later. - ABAC (optional): evaluate simple attribute checks (e.g., token.attrs.env == request.env). This gives BetterAuth a lightweight RBAC today, with room for ABAC later. ## Admin & lifecycle - Create SA: choose owner (org/user), name, tags. - Add credentials: - Add keypair (OAuth/JWT) → returns client_id, kid, private key (show once). - Add OIDC trust → set issuer, subject/claims constraints. - Create simple token (optional) → show once. - Bind permissions: attach roles/scopes to SA (optionally at a resource path). - Rotate keys: allow overlapping validity; show last_used_at to guide cleanup. - Disable / delete with soft-delete window. - Audit: every token mint/introspect and every authz decision emits an event (actor, target, outcome, token.jti). ## Security defaults - Short token TTL (10–30 min) + jti tracking (revocation list for high-risk). - Multiple active keys per SA; last-used timestamps; rotate ≤90 days. - OIDC trusts must pin issuer + at least one constrained claim (e.g., sub or repository). - Hash opaque tokens; show once; rate-limit per SA. ## How this fits BetterAuth today - Concepts: Users & Organizations already exist; SAs simply add a non-human principal anchored to these. - OIDC: Reuse the OIDC Provider plugin to support federation/IDP use and add one STS-like exchange endpoint—no new external infra. - Extensibility: Implement as a BetterAuth plugin adding: - new tables (above), - token mint/exchange handlers, - lightweight RBAC bindings, - admin helpers (create/rotate/disable).
Author
Owner

@dosubot[bot] commented on GitHub (Nov 19, 2025):

Hi, @maelp. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary

  • You requested a "Service Accounts" feature for machine-to-machine API calls with role-based access, independent of individual users.
  • Current API keys are user-tied, which limits enterprise use cases.
  • You provided a detailed draft proposal covering data models, authentication methods, token structure, RBAC, lifecycle, and security.
  • The community has shown strong support and interest in this feature.
  • The issue remains unresolved and open for further discussion or implementation.

Next Steps

  • Please let me know if this feature request is still relevant to the latest version of better-auth by commenting on this issue.
  • If I do not hear back within 7 days, I will automatically close this issue to keep the backlog manageable.

Thank you for your understanding and contribution!

@dosubot[bot] commented on GitHub (Nov 19, 2025): Hi, @maelp. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary** - You requested a "Service Accounts" feature for machine-to-machine API calls with role-based access, independent of individual users. - Current API keys are user-tied, which limits enterprise use cases. - You provided a detailed draft proposal covering data models, authentication methods, token structure, RBAC, lifecycle, and security. - The community has shown strong support and interest in this feature. - The issue remains unresolved and open for further discussion or implementation. **Next Steps** - Please let me know if this feature request is still relevant to the latest version of better-auth by commenting on this issue. - If I do not hear back within 7 days, I will automatically close this issue to keep the backlog manageable. Thank you for your understanding and contribution!
Author
Owner

@reslear commented on GitHub (Nov 19, 2025):

@dosubot not stale

@reslear commented on GitHub (Nov 19, 2025): @dosubot not stale
Author
Owner

@XavierGeerinck commented on GitHub (Jan 15, 2026):

Any update on this from anyone?

@XavierGeerinck commented on GitHub (Jan 15, 2026): Any update on this from anyone?
Author
Owner

@n2p5 commented on GitHub (Jan 29, 2026):

I'm very much interested in this feature and I'd be willing to contribute to help make this happen.

@n2p5 commented on GitHub (Jan 29, 2026): I'm very much interested in this feature and I'd be willing to contribute to help make this happen.
Author
Owner

@metmac commented on GitHub (Feb 20, 2026):

I would also be very interested in seeing this implemented.

@metmac commented on GitHub (Feb 20, 2026): I would also be very interested in seeing this implemented.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1445