mirror of
https://github.com/juanfont/headscale.git
synced 2026-01-12 04:10:32 +01:00
[Bug] Inconsistent handling of OIDC claims #1050
Closed
opened 2025-12-29 02:27:57 +01:00 by adam
·
9 comments
No Branch/Tag Specified
main
update_flake_lock_action
gh-pages
kradalby/release-v0.27.2
dependabot/go_modules/golang.org/x/crypto-0.45.0
dependabot/go_modules/github.com/opencontainers/runc-1.3.3
copilot/investigate-headscale-issue-2788
copilot/investigate-visibility-issue-2788
copilot/investigate-issue-2833
copilot/debug-issue-2846
copilot/fix-issue-2847
dependabot/go_modules/github.com/go-viper/mapstructure/v2-2.4.0
dependabot/go_modules/github.com/docker/docker-28.3.3incompatible
kradalby/cli-experiement3
doc/0.26.1
doc/0.25.1
doc/0.25.0
doc/0.24.3
doc/0.24.2
doc/0.24.1
doc/0.24.0
kradalby/build-docker-on-pr
topic/docu-versioning
topic/docker-kos
juanfont/fix-crash-node-id
juanfont/better-disclaimer
update-contributors
topic/prettier
revert-1893-add-test-stage-to-docs
add-test-stage-to-docs
remove-node-check-interval
fix-empty-prefix
fix-ephemeral-reusable
bug_report-debuginfo
autogroups
logs-to-stderr
revert-1414-topic/fix_unix_socket
rename-machine-node
port-embedded-derp-tests-v2
port-derp-tests
duplicate-word-linter
update-tailscale-1.36
warn-against-apache
ko-fi-link
more-acl-tests
fix-typo-standalone
parallel-nolint
tparallel-fix
rerouting
ssh-changelog-docs
oidc-cleanup
web-auth-flow-tests
kradalby-gh-runner
fix-proto-lint
remove-funding-links
go-1.19
enable-1.30-in-tests
0.16.x
cosmetic-changes-integration
tmp-fix-integration-docker
fix-integration-docker
configurable-update-interval
show-nodes-online
hs2021
acl-syntax-fixes
ts2021-implementation
fix-spurious-updates
unstable-integration-tests
mandatory-stun
embedded-derp
prtemplate-fix
v0.28.0-beta.1
v0.27.2-rc.1
v0.27.1
v0.27.0
v0.27.0-beta.2
v0.27.0-beta.1
v0.26.1
v0.26.0
v0.26.0-beta.2
v0.26.0-beta.1
v0.25.1
v0.25.0
v0.25.0-beta.2
v0.24.3
v0.25.0-beta.1
v0.24.2
v0.24.1
v0.24.0
v0.24.0-beta.2
v0.24.0-beta.1
v0.23.0
v0.23.0-rc.1
v0.23.0-beta.5
v0.23.0-beta.4
v0.23.0-beta3
v0.23.0-beta2
v0.23.0-beta1
v0.23.0-alpha12
v0.23.0-alpha11
v0.23.0-alpha10
v0.23.0-alpha9
v0.23.0-alpha8
v0.23.0-alpha7
v0.23.0-alpha6
v0.23.0-alpha5
v0.23.0-alpha4
v0.23.0-alpha4-docker-ko-test9
v0.23.0-alpha4-docker-ko-test8
v0.23.0-alpha4-docker-ko-test7
v0.23.0-alpha4-docker-ko-test6
v0.23.0-alpha4-docker-ko-test5
v0.23.0-alpha-docker-release-test-debug2
v0.23.0-alpha-docker-release-test-debug
v0.23.0-alpha4-docker-ko-test4
v0.23.0-alpha4-docker-ko-test3
v0.23.0-alpha4-docker-ko-test2
v0.23.0-alpha4-docker-ko-test
v0.23.0-alpha3
v0.23.0-alpha2
v0.23.0-alpha1
v0.22.3
v0.22.2
v0.23.0-alpha-docker-release-test
v0.22.1
v0.22.0
v0.22.0-alpha3
v0.22.0-alpha2
v0.22.0-alpha1
v0.22.0-nfpmtest
v0.21.0
v0.20.0
v0.19.0
v0.19.0-beta2
v0.19.0-beta1
v0.18.0
v0.18.0-beta4
v0.18.0-beta3
v0.18.0-beta2
v0.18.0-beta1
v0.17.1
v0.17.0
v0.17.0-beta5
v0.17.0-beta4
v0.17.0-beta3
v0.17.0-beta2
v0.17.0-beta1
v0.17.0-alpha4
v0.17.0-alpha3
v0.17.0-alpha2
v0.17.0-alpha1
v0.16.4
v0.16.3
v0.16.2
v0.16.1
v0.16.0
v0.16.0-beta7
v0.16.0-beta6
v0.16.0-beta5
v0.16.0-beta4
v0.16.0-beta3
v0.16.0-beta2
v0.16.0-beta1
v0.15.0
v0.15.0-beta6
v0.15.0-beta5
v0.15.0-beta4
v0.15.0-beta3
v0.15.0-beta2
v0.15.0-beta1
v0.14.0
v0.14.0-beta2
v0.14.0-beta1
v0.13.0
v0.13.0-beta3
v0.13.0-beta2
v0.13.0-beta1
upstream/v0.12.4
v0.12.4
v0.12.3
v0.12.2
v0.12.2-beta1
v0.12.1
v0.12.0-beta2
v0.12.0-beta1
v0.11.0
v0.10.8
v0.10.7
v0.10.6
v0.10.5
v0.10.4
v0.10.3
v0.10.2
v0.10.1
v0.10.0
v0.9.3
v0.9.2
v0.9.1
v0.9.0
v0.8.1
v0.8.0
v0.7.1
v0.7.0
v0.6.1
v0.6.0
v0.5.2
v0.5.1
v0.5.0
v0.4.0
v0.3.6
v0.3.5
v0.3.4
v0.3.3
v0.3.2
v0.3.1
v0.3.0
v0.2.2
v0.2.1
v0.2.0
v0.1.1
v0.1.0
Labels
Clear labels
CLI
DERP
DNS
Nix
OIDC
SSH
bug
database
documentation
duplicate
enhancement
faq
good first issue
grants
help wanted
might-come
needs design doc
needs investigation
no-stale-bot
out of scope
performance
policy 📝
pull-request
question
regression
routes
stale
tags
tailscale-feature-gap
well described ❤️
wontfix
Mirrored from GitHub Pull Request
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: starred/headscale#1050
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @nblock on GitHub (Jun 24, 2025).
Is this a support request?
Is there an existing issue for this?
Current Behavior
Recent Headscale versions fetch OIDC claims from the ID token and also query the userinfo endpoint to gather details about the user. This is nice as identity providers seem to reduce the size of tokens lately.
But user authorization happens right before the userinfo endpoint is queried which means that authorization only uses the claims from the ID token. Thus, the data used for authorization and storage might be different, affected config settings:
oidc.allowed_domainsoidc.allowed_usersoidc.allowed_groupsAlso the handling of the
email_verifiedis inconsistent:email_verifiedclaim is not checked. The check foroidc.allowed_domainsandoidc.allowed_usersonly considersemailemail_verified: trueExpected Behavior
Information retrieval from OIDC should be identical for all uses within the codebase.
Steps To Reproduce
oidc.allowed_domains,oidc.allowed_users,oidc.allowed_groupsEnvironment
Runtime environment
Additional notes
email_verifiedfor authorization checks would break existing installs as users without a verified e-mail address would no longer pass the check.issandsubclaim and no longer viaemail. Maybe theemail_verified: trueclaim is no longer that important?@nblock commented on GitHub (Jun 24, 2025):
One possible solution to solve the
email_verifieddifference is to make it configurable whetheremail_verified: trueis required or not. For example:oidc.require_email_verified: true: Process theemail_verifiedclaim and acceptemailonly ifemail_verified: trueoidc.require_email_verified: false: Acceptemailregardless ofemail_verified@fredrikekre commented on GitHub (Jun 26, 2025):
Since the userinfo endpoint is queried unconditionally anyway, couldn't that query simply be moved up to before the
validateOIDCAllowed*calls?I think it still is, at least if you are filtering users based on email or domain?
@james-d-elliott commented on GitHub (Jun 26, 2025):
Please be aware I'm not aware of the particular implementation at play here so some of these comments may not be applicable.
It should be noted that the
scopeparameter, and consequently the granted scopes; has no spec defined effect on an ID Token with the exception of the Implicit Flow which only results in an ID Token. For these reasons the only correct location to obtain claims outside of the ID Token Claims by default is the UserInfo Endpoint.While it's technically permissible for a provider to choose to include additional claims for any reason, it is strongly discouraged by several areas of the spec; and the client should never error if the claims do not exist in the ID Token as long as they are present at the UserInfo endpoint as this is the normative behavior expected of providers for all of the Scope Claims, and is fundamentally a minimum expectation of clients.
It should also be noted that it's critical that a client uses the
issandsubparameters to identify the user. Theemailandpreferred_usernameclaims are not safe to use for this purpose. These claims are useful only for registration and updating client accounts. Clients should store theissandsubclaims in a way that identifies the user locally.Let me know if you would prefer I open separate issues for any of this. I understand while the OP's bug is somewhat related to these points I would understand if this was considered a separate bug as well.
Also feel free to let me know if you're interested in additional information surrounding this. However:
issandsubclaims. If this is not currently implemented then it might be worth prioritizing this.@tecosaur commented on GitHub (Oct 8, 2025):
@fredrikekre do you plan on PRing 8c7518c989d52bddbe15764818776529e3ec1a42 at any point?
@fredrikekre commented on GitHub (Oct 8, 2025):
Merged in
5d8a2c25ea@tecosaur commented on GitHub (Oct 8, 2025):
Ah nice, thanks for that. Seems like this issue could be closed now?
@nblock commented on GitHub (Oct 8, 2025):
Its partially solved,
email_verifiedis handled differently during authorization and storage.@ImpostorKeanu commented on GitHub (Oct 30, 2025):
I like this idea. Best of both worlds.
I'd like to authenticate users via Cloudflare One-time Pin, but it doesn't automatically include
email_verifiedand it doesn't allow adding claims, which means I'll need implement a broker. Making the field configurable solves this problem and it works fine in cases like mine where the email has been verified but can't be signaled using a claim.It'd create complexity, but I've also been considering how to make the implementation flexible.
Consider this invalid config snippet:
Pretty much establish if we can trust claims by performing basic tests. Obviously more complicated, especially if the claim has a dictionary or array.
@nblock commented on GitHub (Dec 18, 2025):
Fixed via
7be2091