[Feature] Support using groups from OIDC in ACLs #917

Open
opened 2025-12-29 02:25:56 +01:00 by adam · 12 comments
Owner

Originally created by @joachimtingvold on GitHub (Jan 22, 2025).

Use case

Fetch/use groups received via OIDC property/scope in ACLs. This way we don't have to manually maintain the group memberships in the ACL.

Description

This is a continuation from the discussion in #1121.

The feature does not need to implement full support for all OIDC providers (which would complicate things). This feature can simply be done by having a setting in headscale where you can specify the name of the OIDC property that contains the array of groups the user is a member of. If that is a non-empty list of groups, headscale can use that list of groups to evaluate what permissions the user will have. If a group received from OIDC is not present in headscale ACL config, it can simply be ignored.

Since headscale already have a field to define the OIDC scope in the configuration, no further changes on that aspect is needed. Simply adding an option like group_property: groups or similar in the oidc section of config.yaml would be sufficient from the configuration perspective.

Example config and JSON blobs using Authentik added below. Given these examples, the expectation would be that my user will have access to the host 10.1.1.1 (server1), even if there is no statically configured group "server1-admins" in acls.hujson.

OIDC Discover response:

{
  "issuer": "https://login.foo.bar/application/o/headscale/",
  "authorization_endpoint": "https://login.foo.bar/application/o/authorize/",
  "token_endpoint": "https://login.foo.bar/application/o/token/",
  "userinfo_endpoint": "https://login.foo.bar/application/o/userinfo/",
  "end_session_endpoint": "https://login.foo.bar/application/o/headscale/end-session/",
  "introspection_endpoint": "https://login.foo.bar/application/o/introspect/",
  "revocation_endpoint": "https://login.foo.bar/application/o/revoke/",
  "device_authorization_endpoint": "https://login.foo.bar/application/o/device/",
  "response_types_supported": [
    "code",
    "id_token",
    "id_token token",
    "code token",
    "code id_token",
    "code id_token token"
  ],
  "response_modes_supported": [
    "query",
    "fragment",
    "form_post"
  ],
  "jwks_uri": "https://login.foo.bar/application/o/headscale/jwks/",
  "grant_types_supported": [
    "authorization_code",
    "refresh_token",
    "implicit",
    "client_credentials",
    "password",
    "urn:ietf:params:oauth:grant-type:device_code"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "subject_types_supported": [
    "public"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_post",
    "client_secret_basic"
  ],
  "acr_values_supported": [
    "goauthentik.io/providers/oauth2/default"
  ],
  "scopes_supported": [
    "profile",
    "openid",
    "email"
  ],
  "request_parameter_supported": false,
  "claims_supported": [
    "sub",
    "iss",
    "aud",
    "exp",
    "iat",
    "auth_time",
    "acr",
    "amr",
    "nonce",
    "email",
    "email_verified",
    "name",
    "given_name",
    "family_name",
    "preferred_username",
    "nickname",
    "groups"
  ],
  "claims_parameter_supported": false,
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ]
}

OIDC userinfo response:

{
  "sub": "65cbb96f55633865c19c6cccb8b2bb9e575dc7d7b53d3e9bbcaf4fa7bfcda8a8",
  "email": "joachim@foo.bar",
  "email_verified": true,
  "name": "Joachim Tingvold",
  "given_name": "Joachim",
  "family_name": "Tingvold",
  "preferred_username": "jocke",
  "nickname": "jocke",
  "groups": [
    "vpn-admins",
    "server-admins",
    "server1-admins",
    "server2-admins",
    "bms-system-admins"
  ],
  "nonce": "1a07de44633382b528b6f2f114b9ff10"
}

OIDC JWT payload:

{
  "aud": "96crVkZhfTueYrzoP9jup8WPDqbGrrtFkmrbGeN3",
  "name": "Joachim Tingvold",
  "email": "joachim@foo.bar",
  "sid": "7cc65765730adc54a736e4e774bc73f4f44d37d02815dcdd3cc9220654c79823",
  "auth_time": 1737550394,
  "acr": "goauthentik.io/providers/oauth2/default",
  "amr": [
    "pwd",
    "mfa"
  ],
  "email_verified": true,
  "given_name": "Joachim",
  "family_name": "Tingvold",
  "preferred_username": "jocke",
  "nickname": "jocke",
  "azp": "96crVkZhfTueYrzoP9jup8WPDqbGrrtFkmrbGeN3",
  "sub": "65cbb96f55633865c19c6cccb8b2bb9e575dc7d7b53d3e9bbcaf4fa7bfcda8a8",
  "iat": 1737550500,
  "uid": "PM9TnbyTc7BJmvZPQm6tz9HCnLLMRqAZNUWZG8WR",
  "groups": [
    "vpn-admins",
    "server-admins",
    "server1-admins",
    "server2-admins",
    "bms-system-admins"
  ],
  "nonce": "1a07de44633382b528b6f2f114b9ff10",
  "iss": "https://login.keklolwtf.no/application/o/headscale/",
  "exp": 1737550800
}

oidc-section of config.yaml:

oidc:
  only_start_if_oidc_is_available: true
  issuer: "https://login.foo.bar/application/o/headscale/"
  client_id: "some client id"
  client_secret: "super secret stuff"
  expiry: 7d
  use_expiry_from_token: false
  scope: ["openid", "profile", "email"]
  strip_email_domain: false
  group_property: groups

Relevant parts of acls.hujson:

{
  "groups": {
  },
  "tagOwners": {
  },
  "hosts": {
    "server1": "10.1.1.1",
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:server1-admins"],
      "dst": [
        "server1:*",
      ]
    }
  ],
}

Originally created by @joachimtingvold on GitHub (Jan 22, 2025). ### Use case Fetch/use groups received via OIDC property/scope in ACLs. This way we don't have to manually maintain the group memberships in the ACL. ### Description This is a continuation from the discussion in #1121. The feature does not need to implement full support for all OIDC providers (which would complicate things). This feature can simply be done by having a setting in headscale where you can specify the name of the OIDC property that contains the array of groups the user is a member of. If that is a non-empty list of groups, headscale can use that list of groups to evaluate what permissions the user will have. If a group received from OIDC is not present in headscale ACL config, it can simply be ignored. Since headscale already have a field to define the OIDC scope in the configuration, no further changes on that aspect is needed. Simply adding an option like `group_property: groups` or similar in the `oidc` section of `config.yaml` would be sufficient from the configuration perspective. Example config and JSON blobs using Authentik added below. Given these examples, the expectation would be that my user will have access to the host 10.1.1.1 (server1), even if there is no statically configured group "server1-admins" in `acls.hujson`. <details> <summary>OIDC Discover response:</summary> ``` { "issuer": "https://login.foo.bar/application/o/headscale/", "authorization_endpoint": "https://login.foo.bar/application/o/authorize/", "token_endpoint": "https://login.foo.bar/application/o/token/", "userinfo_endpoint": "https://login.foo.bar/application/o/userinfo/", "end_session_endpoint": "https://login.foo.bar/application/o/headscale/end-session/", "introspection_endpoint": "https://login.foo.bar/application/o/introspect/", "revocation_endpoint": "https://login.foo.bar/application/o/revoke/", "device_authorization_endpoint": "https://login.foo.bar/application/o/device/", "response_types_supported": [ "code", "id_token", "id_token token", "code token", "code id_token", "code id_token token" ], "response_modes_supported": [ "query", "fragment", "form_post" ], "jwks_uri": "https://login.foo.bar/application/o/headscale/jwks/", "grant_types_supported": [ "authorization_code", "refresh_token", "implicit", "client_credentials", "password", "urn:ietf:params:oauth:grant-type:device_code" ], "id_token_signing_alg_values_supported": [ "RS256" ], "subject_types_supported": [ "public" ], "token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic" ], "acr_values_supported": [ "goauthentik.io/providers/oauth2/default" ], "scopes_supported": [ "profile", "openid", "email" ], "request_parameter_supported": false, "claims_supported": [ "sub", "iss", "aud", "exp", "iat", "auth_time", "acr", "amr", "nonce", "email", "email_verified", "name", "given_name", "family_name", "preferred_username", "nickname", "groups" ], "claims_parameter_supported": false, "code_challenge_methods_supported": [ "plain", "S256" ] } ``` </details> <details> <summary>OIDC userinfo response:</summary> ``` { "sub": "65cbb96f55633865c19c6cccb8b2bb9e575dc7d7b53d3e9bbcaf4fa7bfcda8a8", "email": "joachim@foo.bar", "email_verified": true, "name": "Joachim Tingvold", "given_name": "Joachim", "family_name": "Tingvold", "preferred_username": "jocke", "nickname": "jocke", "groups": [ "vpn-admins", "server-admins", "server1-admins", "server2-admins", "bms-system-admins" ], "nonce": "1a07de44633382b528b6f2f114b9ff10" } ``` </details> <details> <summary>OIDC JWT payload:</summary> ``` { "aud": "96crVkZhfTueYrzoP9jup8WPDqbGrrtFkmrbGeN3", "name": "Joachim Tingvold", "email": "joachim@foo.bar", "sid": "7cc65765730adc54a736e4e774bc73f4f44d37d02815dcdd3cc9220654c79823", "auth_time": 1737550394, "acr": "goauthentik.io/providers/oauth2/default", "amr": [ "pwd", "mfa" ], "email_verified": true, "given_name": "Joachim", "family_name": "Tingvold", "preferred_username": "jocke", "nickname": "jocke", "azp": "96crVkZhfTueYrzoP9jup8WPDqbGrrtFkmrbGeN3", "sub": "65cbb96f55633865c19c6cccb8b2bb9e575dc7d7b53d3e9bbcaf4fa7bfcda8a8", "iat": 1737550500, "uid": "PM9TnbyTc7BJmvZPQm6tz9HCnLLMRqAZNUWZG8WR", "groups": [ "vpn-admins", "server-admins", "server1-admins", "server2-admins", "bms-system-admins" ], "nonce": "1a07de44633382b528b6f2f114b9ff10", "iss": "https://login.keklolwtf.no/application/o/headscale/", "exp": 1737550800 } ``` </details> <details> <summary>oidc-section of config.yaml:</summary> ``` oidc: only_start_if_oidc_is_available: true issuer: "https://login.foo.bar/application/o/headscale/" client_id: "some client id" client_secret: "super secret stuff" expiry: 7d use_expiry_from_token: false scope: ["openid", "profile", "email"] strip_email_domain: false group_property: groups ``` </details> <details> <summary>Relevant parts of acls.hujson:</summary> ``` { "groups": { }, "tagOwners": { }, "hosts": { "server1": "10.1.1.1", }, "acls": [ { "action": "accept", "src": ["group:server1-admins"], "dst": [ "server1:*", ] } ], } ``` </details>
adam added the enhancementno-stale-bot labels 2025-12-29 02:25:56 +01:00
Author
Owner

@Nathanael-Mtd commented on GitHub (Jan 23, 2025):

Useful feature for many of us !

About the implementation idea, it's more a user-group import and store to ACL file which be needed, when OIDC (re-)login to headscale is done (first time login or on expiration).
Because that's only during this step OIDC user info is handled by Headscale, not at every Tailscale client startup.

Like that we can dynamically fill the ACL policy with user mappings to groups, and be handle manual user groups changes when expiration is too far.
But maybe it can have an impact on Headscale if there are many ACL reloads.

Overall there are some limitations with OpenID provisioning because we can't handle automatic groups changes and account lock/deletion before node expiration.

@Nathanael-Mtd commented on GitHub (Jan 23, 2025): Useful feature for many of us ! About the implementation idea, it's more a user-group import and store to ACL file which be needed, when OIDC (re-)login to headscale is done (first time login or on expiration). Because that's only during this step OIDC user info is handled by Headscale, not at every Tailscale client startup. Like that we can dynamically fill the ACL policy with user mappings to groups, and be handle manual user groups changes when expiration is too far. But maybe it can have an impact on Headscale if there are many ACL reloads. Overall there are some limitations with OpenID provisioning because we can't handle automatic groups changes and account lock/deletion before node expiration.
Author
Owner

@joachimtingvold commented on GitHub (Jan 23, 2025):

About the implementation idea, it's more a user-group import and store to ACL file which be needed, when OIDC (re-)login to headscale is done (first time login or on expiration). Because that's only during this step OIDC user info is handled by Headscale, not at every Tailscale client startup.

Fair enough. Would we need to really write/update acls.hujson for every login? How does Headscale handle node expirations during a reload/restart of the headscale service itself? Does node expire due to that (i.e. trigger a relogin), or would it actually maintain already cached nodes? If they expire, the group(s) could then technically also be held in memory? (rather than writing/altering acls.hujson directly). If nodes are not expired due to a service restart, then you would of course need to do so.

Like that we can dynamically fill the ACL policy with user mappings to groups, and be handle manual user groups changes when expiration is too far. But maybe it can have an impact on Headscale if there are many ACL reloads.

There might be a potential scaling issue in play here, depending on how many users there are on a given headscale instance. I would wager that this would not be the case for the majority of the headscale instances out there (in which case it would probably make more sense for them to have scripts that would populate acls.hujson groups at regular intervals that fetches group membership from whatever IdP they're using).

Overall there are some limitations with OpenID provisioning because we can't handle automatic groups changes and account lock/deletion before node expiration.

That would also be fine for most usecases for this, I would say.

@joachimtingvold commented on GitHub (Jan 23, 2025): > About the implementation idea, it's more a user-group import and store to ACL file which be needed, when OIDC (re-)login to headscale is done (first time login or on expiration). Because that's only during this step OIDC user info is handled by Headscale, not at every Tailscale client startup. Fair enough. Would we need to really write/update `acls.hujson` for every login? How does Headscale handle node expirations during a reload/restart of the headscale service itself? Does node expire due to that (i.e. trigger a relogin), or would it actually maintain already cached nodes? If they expire, the group(s) could then technically also be held in memory? (rather than writing/altering `acls.hujson` directly). If nodes are not expired due to a service restart, then you would of course need to do so. > Like that we can dynamically fill the ACL policy with user mappings to groups, and be handle manual user groups changes when expiration is too far. But maybe it can have an impact on Headscale if there are many ACL reloads. There might be a potential scaling issue in play here, depending on how many users there are on a given headscale instance. I would wager that this would not be the case for the majority of the headscale instances out there (in which case it would probably make more sense for them to have scripts that would populate `acls.hujson` groups at regular intervals that fetches group membership from whatever IdP they're using). > Overall there are some limitations with OpenID provisioning because we can't handle automatic groups changes and account lock/deletion before node expiration. That would also be fine for most usecases for this, I would say.
Author
Owner

@kradalby commented on GitHub (Jan 23, 2025):

It sounds like @Nathanael-Mtd is essentially describing SCIM v2.

I think that would be an unrealistic undertaking, and out of scope.

A reasonable compromise sounds like we are able to sync in groups, and they can be used. The admin can then decide the "tolerance" they how for groups being out of sync by setting a short or long expiry time.

@kradalby commented on GitHub (Jan 23, 2025): It sounds like @Nathanael-Mtd is essentially describing [SCIM v2](https://scim.cloud). I think that would be an unrealistic undertaking, and out of scope. A reasonable compromise sounds like we are able to sync in groups, and they can be used. The admin can then decide the "tolerance" they how for groups being out of sync by setting a short or long expiry time.
Author
Owner

@IamTaoChen commented on GitHub (Jan 24, 2025):

I don’t think it’s a good idea to sync groups directly from OIDC. Headscale cannot determine when a user is removed from a group. For example:

  1. User A belongs to the manager group when they log in.
  2. The administrator removes A from the manager group within 180 days (the default duration of a Headscale token).
  3. A will still appear as part of the manager group during that time.

Additionally, we cannot disable users from OIDC during this period.

Perhaps we could create an API to manage groups, storing group information in a separate table. The ACL module can maintain its own groups, but we would need an additional step to generate the real ACL (by combining the group information from the database with the ACL file).

When updating the real ACL to the client, we could define a policy, such as on update, every 24 hours, etc.

With this functionality, users could create their own scripts to sync users and groups from their backend systems. For example, I would use LDAP to align users’ groups. we only need to give some examples, and we don't need to maintain the oidc groups

@IamTaoChen commented on GitHub (Jan 24, 2025): I don’t think it’s a good idea to sync groups directly from OIDC. Headscale cannot determine when a user is removed from a group. For example: 1. User A belongs to the manager group when they log in. 2. The administrator removes A from the manager group within 180 days (the default duration of a Headscale token). 3. A will still appear as part of the manager group during that time. Additionally, we cannot disable users from OIDC during this period. Perhaps we could create an API to manage groups, storing group information in a separate table. The ACL module can maintain its own groups, but we would need an additional step to generate the real ACL (by combining the group information from the database with the ACL file). When updating the real ACL to the client, we could define a policy, such as on update, every 24 hours, etc. With this functionality, users could create their own scripts to sync users and groups from their backend systems. For example, I would use LDAP to align users’ groups. we only need to give some examples, and we don't need to maintain the oidc groups
Author
Owner

@Nathanael-Mtd commented on GitHub (Jan 24, 2025):

About how Headscale works now, it require adding users on groups by hand. For a little improvment, before integrating API or SCIMv2 (maybe one day or never), we can start by only importing users into ACL groups if the mapping with OIDC groups recieved match.

It doesn't change the need for administrators to handle group changes and removals by hand, but we can accelerate user onboarding in a simple way.

@Nathanael-Mtd commented on GitHub (Jan 24, 2025): About how Headscale works now, it require adding users on groups by hand. For a little improvment, before integrating API or SCIMv2 (maybe one day or never), we can start by only importing users into ACL groups if the mapping with OIDC groups recieved match. It doesn't change the need for administrators to handle group changes and removals by hand, but we can accelerate user onboarding in a simple way.
Author
Owner

@joachimtingvold commented on GitHub (Jan 24, 2025):

It doesn't change the need for administrators to handle group changes and removals by hand, but we can accelerate user onboarding in a simple way.

It's the onboarding that is the most "cumbersome" today for the installations that I manage. Offboarding is a completely different beast.

[…] we can start by only importing users into ACL groups if the mapping with OIDC groups recieved match.

It should probably ignore groups received from OIDC not added in acls.hujson. If you get group1,group2,group3 from OIDC, but only have group1,group3 specified in acls.hujson, the user should be added to group1 and group3.

Would another option be to store the "array of groups" received from OIDC with the "node/machine" when logging in/refreshing the node? (rather than having to write to acls.hujson every time a user logs in). If you need to offboard a user/group, you could just invalidate the node/machine (which you would want to anyways, probably).

@joachimtingvold commented on GitHub (Jan 24, 2025): > It doesn't change the need for administrators to handle group changes and removals by hand, but we can accelerate user onboarding in a simple way. It's the onboarding that is the most "cumbersome" today for the installations that I manage. Offboarding is a completely different beast. > […] we can start by only importing users into ACL groups if the mapping with OIDC groups recieved match. It should probably ignore groups received from OIDC not added in `acls.hujson`. If you get `group1,group2,group3` from OIDC, but only have `group1,group3` specified in `acls.hujson`, the user should be added to group1 and group3. Would another option be to store the "array of groups" received from OIDC with the "node/machine" when logging in/refreshing the node? (rather than having to write to `acls.hujson` every time a user logs in). If you need to offboard a user/group, you could just invalidate the node/machine (which you would want to anyways, probably).
Author
Owner

@Nathanael-Mtd commented on GitHub (Jan 24, 2025):

It should probably ignore groups received from OIDC not added in acls.hujson. If you get group1,group2,group3 from OIDC, but only have group1,group3 specified in acls.hujson, the user should be added to group1 and group3.

Would another option be to store the "array of groups" received from OIDC with the "node/machine" when logging in/refreshing the node? (rather than having to write to acls.hujson every time a user logs in). If you need to offboard a user/group, you could just invalidate the node/machine (which you would want to anyways, probably).

Yeah I find that better ignoring groups recieved from OIDC and not defined in headscale ACLs, because it can be massive.

Another idea is a mapping file (or section in headscale config) to associate groups from OIDC to the ones in ACLs, to handle group names differences from OIDC.

@Nathanael-Mtd commented on GitHub (Jan 24, 2025): > It should probably ignore groups received from OIDC not added in `acls.hujson`. If you get `group1,group2,group3` from OIDC, but only have `group1,group3` specified in `acls.hujson`, the user should be added to group1 and group3. > > Would another option be to store the "array of groups" received from OIDC with the "node/machine" when logging in/refreshing the node? (rather than having to write to `acls.hujson` every time a user logs in). If you need to offboard a user/group, you could just invalidate the node/machine (which you would want to anyways, probably). Yeah I find that better ignoring groups recieved from OIDC and not defined in headscale ACLs, because it can be massive. Another idea is a mapping file (or section in headscale config) to associate groups from OIDC to the ones in ACLs, to handle group names differences from OIDC.
Author
Owner

@github-actions[bot] commented on GitHub (Apr 25, 2025):

This issue is stale because it has been open for 90 days with no activity.

@github-actions[bot] commented on GitHub (Apr 25, 2025): This issue is stale because it has been open for 90 days with no activity.
Author
Owner

@lambdaupb commented on GitHub (Jun 8, 2025):

Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically?

https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess

https://www.keycloak.org/docs/latest/server_admin/index.html#_offline-access

Seems to be this is exactly for this use-case.

@lambdaupb commented on GitHub (Jun 8, 2025): Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically? https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess https://www.keycloak.org/docs/latest/server_admin/index.html#_offline-access Seems to be this is exactly for this use-case.
Author
Owner

@fschrempf commented on GitHub (Jul 31, 2025):

Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically?

Sounds interesting, but I guess it wouldn't really scale for instances with a large number of users. You would need to periodically query the endpoint for each user.

Probably that's one of the reasons why some services (e.g. OpenCloud) allow to combine OIDC for SSO with LDAP for user information and group memberships (with the IdP using the same LDAP as user backend).

@fschrempf commented on GitHub (Jul 31, 2025): > Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically? Sounds interesting, but I guess it wouldn't really scale for instances with a large number of users. You would need to periodically query the endpoint for each user. Probably that's one of the reasons why some services (e.g. OpenCloud) allow to combine OIDC for SSO with LDAP for user information and group memberships (with the IdP using the same LDAP as user backend).
Author
Owner

@lambdaupb commented on GitHub (Aug 1, 2025):

Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically?

Sounds interesting, but I guess it wouldn't really scale for instances with a large number of users. You would need to periodically query the endpoint for each user.

Probably that's one of the reasons why some services (e.g. OpenCloud) allow to combine OIDC for SSO with LDAP for user information and group memberships (with the IdP using the same LDAP as user backend).

What would be different with LDAP? You would also need to periodically query the LDAP Server for updates as well.

@lambdaupb commented on GitHub (Aug 1, 2025): > > Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically? > > Sounds interesting, but I guess it wouldn't really scale for instances with a large number of users. You would need to periodically query the endpoint for each user. > > Probably that's one of the reasons why some services (e.g. OpenCloud) allow to combine OIDC for SSO with LDAP for user information and group memberships (with the IdP using the same LDAP as user backend). What would be different with LDAP? You would also need to periodically query the LDAP Server for updates as well.
Author
Owner

@Nathanael-Mtd commented on GitHub (Aug 1, 2025):

Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically?

Sounds interesting, but I guess it wouldn't really scale for instances with a large number of users. You would need to periodically query the endpoint for each user.

Probably that's one of the reasons why some services (e.g. OpenCloud) allow to combine OIDC for SSO with LDAP for user information and group memberships (with the IdP using the same LDAP as user backend).

What would be different with LDAP? You would also need to periodically query the LDAP Server for updates as well.

When you query LDAP server to get users info, you don't send one request per user, but one for whole users.
Also, offline tokens and whole OIDC sessions can be revoked, how will you handle that, except by forcing tailscale users to re-login to headscale to refresh them ?

That's not something very reliable, better try to integrate SCIM instead.

@Nathanael-Mtd commented on GitHub (Aug 1, 2025): > > > Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically? > > > > Sounds interesting, but I guess it wouldn't really scale for instances with a large number of users. You would need to periodically query the endpoint for each user. > > > > Probably that's one of the reasons why some services (e.g. OpenCloud) allow to combine OIDC for SSO with LDAP for user information and group memberships (with the IdP using the same LDAP as user backend). > > What would be different with LDAP? You would also need to periodically query the LDAP Server for updates as well. When you query LDAP server to get users info, you don't send one request per user, but one for whole users. Also, offline tokens and whole OIDC sessions can be revoked, how will you handle that, except by forcing tailscale users to re-login to headscale to refresh them ? That's not something very reliable, better try to integrate SCIM instead.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#917