Hanko v3.1 introduces Organizations and Roles for building B2B applications, alongside Custom Claims and Mapping for bringing identity provider attributes into your application. This release also adds session lifecycle webhooks, improves user imports, and includes several security fixes.
Organizations and Roles
You can now group users into organizations within a Hanko tenant and assign roles to their memberships. This gives applications a foundation for managing customer accounts, teams, and organization-specific access.
Roles are defined at the tenant level and assigned to users within individual organizations. For example, a user can be an admin in one organization and a member in another, while your application uses the same role definitions across both.
The new APIs let you:
- Create, list, update, and delete organizations and roles.
- Add users to organizations and manage their role assignments.
- Create users with organization memberships and roles in a single request.
- Retrieve organization memberships and roles through the user APIs and session validation.
- Check whether the current session holds any of a specified set of roles in an organization using
POST /organizations/roles/check.
Your application can use these role checks to enforce access to organization-specific resources. Organizations, memberships, and role bindings are scoped to their Hanko tenant.
Custom Claims and Mapping
Hanko now supports tenant-defined custom claims, with mappings from SAML attributes and custom OIDC/OAuth2 provider claims. This lets you normalize identity data from different providers and make it available to your application through Hanko.
For example, different customer identity providers might represent a department or group membership under different attribute names. You can map those attributes to consistent Hanko custom claims such as department or groups, then include them in your session JWT.
Define and map your claims
Declare claim names, types, and optional descriptions under custom_claims.definitions. Definitions support scalar values and lists of scalars, with up to 50 custom claim definitions per tenant.
Configure mappings for each connection:
- SAML: Map identity provider attributes using
attribute_map.custom. - Custom OIDC/OAuth2 providers: Map values from the provider’s raw claims using
custom_claim_mapping, with GJSON paths for selecting values.
Mappings are validated against your claim definitions when saved, helping catch configuration errors before users log in.
Use claims in your application
Custom claim values are resolved and stored when a user logs in through a mapped connection. You can include them in JWT templates using references such as:
{{ .User.CustomClaims "department" }}
When a template value consists solely of a custom claim reference, Hanko preserves its underlying type, including numbers, booleans, and arrays.
Stored claims are also available through the read-only Admin API endpoint GET /users/:id/custom_claims. The new user.update.custom_claims webhook event lets your application react when claim values change.
Claim updates track their source connection: if that connection stops supplying a claim it owns, the value is cleared. An unrelated connection omitting the same claim does not clear it.
Important for self-hosters: client IP resolution has changed
This release fixes a rate limit bypass caused by trusting client-supplied X-Forwarded-For and X-Real-IP headers.
Hanko now defaults to direct IP extraction, using the connecting network peer’s address and ignoring forwarding headers.
If you run Hanko behind a reverse proxy, load balancer, or ingress controller, update your configuration so rate limits continue to apply to individual client IPs. Without this change, clients connecting through the same proxy may share a rate limit bucket.
For a proxy using X-Forwarded-For:
server:
ip:
extractor: x_forwarded_for
trusted_proxies:
- "10.0.1.0/24" # Replace with your proxy's actual CIDR rangeUse x_real_ip instead if your proxy provides the client address through X-Real-IP.
Both header-based modes require trusted_proxies. Configure only the address ranges of your own proxies, ensure they strip or overwrite client-supplied forwarding headers, and prevent clients from reaching Hanko directly when using these modes.
Deployments where clients connect directly to Hanko can keep the default direct mode.
More improvements
- Session lifecycle webhooks: Subscribe to session creation and deletion events, including login, administrative creation, logout, revocation, idle timeout, and session-limit eviction. See [#2838](#2838).
- Tenant-scoped user IDs: The same caller-supplied user ID can now be used in separate tenants, simplifying imports and migrations. Existing users retain their externally visible IDs. See [#2826](#2826).
- Improved password imports: Added support for bcrypt hashes using the
$2b$format. - Longer SAML certificate validity: Newly generated SP certificates now default to a validity of 10 years.
- Additional translations: Added Korean frontend translations and Polish email translations.
- Developer experience: Added a static passcode generator for tests, expanded Flow API type documentation, and embedded backend version information in container images.
Security and reliability
This release also prevents TOTP code replay, fixes a third-party redirect allowlist bypass and cross-tenant data leakage, and adds validation to prevent linking unverified third-party provider email addresses.
Further fixes improve SAML error handling, third-party login redirects, cancelled passkey registration handling, and support for long user-agent strings.
Thanks to everyone who contributed code, translations, bug reports, and dependency updates to this release!
What's Changed
- feat(i18n): add Korean (ko) translation by @moduvoice in #2677
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.25 to 1.32.26 in /backend by @dependabot[bot] in #2711
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.53.4 to 1.54.0 in /backend by @dependabot[bot] in #2709
- chore(deps-dev): bump sigstore from 4.1.0 to 4.1.1 in /frontend by @dependabot[bot] in #2717
- chore(deps): bump github.com/jackc/pgx/v5 from 5.9.2 to 5.10.0 in /backend by @dependabot[bot] in #2691
- chore(deps): bump github.com/lestrrat-go/jwx/v2 from 2.1.6 to 2.1.7 in /backend by @dependabot[bot] in #2714
- chore(deps-dev): bump eslint-plugin-prettier from 5.5.5 to 5.5.6 in /frontend by @dependabot[bot] in #2730
- chore(deps): bump golang.org/x/text from 0.38.0 to 0.40.0 in /backend by @dependabot[bot] in #2729
- chore(deps): bump github.com/coreos/go-oidc/v3 from 3.19.0 to 3.20.0 in /backend by @dependabot[bot] in #2727
- chore(deps): bump github.com/go-redsync/redsync/v4 from 4.16.0 to 4.17.0 in /backend by @dependabot[bot] in #2726
- chore(deps): bump golang.org/x/crypto from 0.53.0 to 0.54.0 in /backend by @dependabot[bot] in #2728
- refactor(thirdparty): propagate context and fix duplicate defer in oauth providers by @glatinone in #2725
- fix: prevent TOTP code replay by @lfleischmann in #2723
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.26 to 1.32.30 in /backend by @dependabot[bot] in #2742
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.54.0 to 1.54.1 in /backend by @dependabot[bot] in #2741
- chore(deps): bump github.com/beevik/etree from 1.6.0 to 1.7.0 in /backend by @dependabot[bot] in #2740
- chore(deps-dev): bump ts-loader from 9.5.7 to 9.6.2 in /frontend by @dependabot[bot] in #2736
- chore(deps): bump actions/setup-node from 6 to 7 by @dependabot[bot] in #2743
- chore(deps): bump zone.js from 0.16.1 to 0.16.2 in /frontend by @dependabot[bot] in #2746
- chore(deps): bump jwks-rsa from 4.0.1 to 4.1.0 in /frontend by @dependabot[bot] in #2755
- chore(deps-dev): bump turbo from 2.9.16 to 2.10.5 in /frontend by @dependabot[bot] in #2756
- chore: update quickstart go versions by @lfleischmann in #2757
- chore(deps): bump vue-router from 5.0.4 to 5.2.0 in /frontend by @dependabot[bot] in #2754
- chore(deps): bump golang.org/x/net from 0.47.0 to 0.55.0 in /quickstart by @dependabot[bot] in #2715
- chore(deps): bump golang.org/x/crypto from 0.51.0 to 0.52.0 in /quickstart by @dependabot[bot] in #2724
- chore(deps): bump markdown-it and jsdoc in /frontend by @dependabot[bot] in #2689
- fix: sal providers migration attribute map type by @lfleischmann in #2758
- chore(deps-dev): bump vue-tsc from 3.2.6 to 3.3.7 in /frontend by @dependabot[bot] in #2761
- chore(deps): bump github.com/sethvargo/go-limiter from 1.1.0 to 1.2.0 in /backend by @dependabot[bot] in #2760
- chore(deps): bump body-parser in /frontend by @dependabot[bot] in #2766
- chore(deps): bump shell-quote from 1.8.4 to 1.10.0 in /frontend by @dependabot[bot] in #2767
- chore(deps-dev): bump tar from 7.5.16 to 7.5.20 in /frontend by @dependabot[bot] in #2769
- chore(deps): bump actions/setup-go from 6 to 7 by @dependabot[bot] in #2770
- chore(deps-dev): bump ts-jest from 29.4.9 to 29.4.11 in /frontend by @dependabot[bot] in #2773
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.30 to 1.32.31 in /backend by @dependabot[bot] in #2775
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.54.1 to 1.55.0 in /backend by @dependabot[bot] in #2777
- fix: deflake TestConcurrentLockAttempts with a start barrier by @lfleischmann in #2747
- chore(deps): bump react-router-dom from 7.17.0 to 7.18.1 in /frontend by @dependabot[bot] in #2776
- chore(deps): bump immutable from 5.1.5 to 5.1.9 in /frontend by @dependabot[bot] in #2778
- chore(deps): bump svgo from 2.8.0 to 2.8.3 in /frontend by @dependabot[bot] in #2779
- chore(deps): bump fast-uri from 3.1.2 to 3.1.4 in /frontend by @dependabot[bot] in #2780
- chore(deps-dev): bump hono from 4.12.26 to 4.12.31 in /frontend by @dependabot[bot] in #2781
- chore(deps): bump next from 16.2.6 to 16.2.11 in /frontend by @dependabot[bot] in #2782
- chore(deps): bump ws from 8.18.3 to 8.21.1 in /frontend by @dependabot[bot] in #2785
- chore(deps): bump node-forge from 1.3.3 to 1.4.0 in /frontend by @dependabot[bot] in #2786
- chore(deps-dev): bump flatted from 3.3.3 to 3.4.3 in /frontend by @dependabot[bot] in #2787
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.31 to 1.32.32 in /backend by @dependabot[bot] in #2791
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.55.0 to 1.55.1 in /backend by @dependabot[bot] in #2792
- fix: thirdparty allowed redirect bypass by @lfleischmann in #2734
- fix: rate limit bypass by @lfleischmann in #2739
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.55.1 to 1.55.2 in /backend by @dependabot[bot] in #2794
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.32 to 1.32.33 in /backend by @dependabot[bot] in #2795
- chore(deps-dev): bump sass from 1.100.0 to 1.102.0 in /frontend by @dependabot[bot] in #2803
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.55.2 to 1.55.3 in /backend by @dependabot[bot] in #2812
- chore(deps-dev): bump ip-address from 10.2.0 to 10.4.0 in /frontend by @dependabot[bot] in #2806
- chore(deps): bump fast-uri from 3.1.4 to 3.1.5 in /frontend by @dependabot[bot] in #2807
- fix: make concurrent test deterministic with an attempt barrier by @lfleischmann in #2825
- chore: remove user retrieval functions by @lfleischmann in #2789
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.33 to 1.32.35 in /backend by @dependabot[bot] in #2811
- chore(deps-dev): bump turbo from 2.10.5 to 2.10.9 in /frontend by @dependabot[bot] in #2809
- chore(deps): bump github.com/knadh/koanf/v2 from 2.3.5 to 2.3.6 in /backend by @dependabot[bot] in #2814
- chore(deps): bump dotenv from 17.4.1 to 17.4.2 in /frontend by @dependabot[bot] in #2815
- chore(deps): bump react-router and react-router-dom in /frontend by @dependabot[bot] in #2822
- chore(deps): bump github.com/knadh/koanf/parsers/yaml from 1.1.0 to 1.1.1 in /backend by @dependabot[bot] in #2813
- chore(deps): bump github.com/knadh/koanf/parsers/json from 1.0.0 to 1.0.1 in /backend by @dependabot[bot] in #2816
- chore(deps-dev): bump webpack-cli from 7.0.3 to 7.2.2 in /frontend by @dependabot[bot] in #2818
- chore(deps-dev): bump hono from 4.12.31 to 4.13.1 in /frontend by @dependabot[bot] in #2821
- chore(deps): bump github.com/knadh/koanf/providers/rawbytes from 1.0.0 to 1.0.1 in /backend by @dependabot[bot] in #2835
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.55.3 to 1.55.4 in /backend by @dependabot[bot] in #2831
- chore(deps-dev): bump webpack from 5.106.1 to 5.109.2 in /frontend by @dependabot[bot] in #2827
- feat: emit webhook events on session creation and deletion by @lfleischmann in #2838
- docs: add JSDoc coverage for flow-api types and reorganize categories by @lfleischmann in #2790
- fix: don't redirect with undefined url when thirdparty state has an error by @lfleischmann in #2844
- fix(frontend-sdk): don't report cancelled passkey creation as already-existing by @ousamabenyounes in #2824
- chore(deps): bump github.com/russellhaering/goxmldsig from 1.6.0 to 1.6.1 in /backend by @dependabot[bot] in #2832
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.55.4 to 1.55.5 in /backend by @dependabot[bot] in #2847
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.35 to 1.32.36 in /backend by @dependabot[bot] in #2846
- feat(backend): add polish (pl) email translations by @ftn0 in #2839
- chore(deps): bump github.com/russellhaering/gosaml2 from 0.11.0 to 0.12.0 in /backend by @dependabot[bot] in #2833
- fix: cross tenant leakages by @lfleischmann in #2865
- fix: add support for bcrypt "$2b$" format in import password validation by @FreddyDevelop in #2850
- chore(deps-dev): bump hono from 4.13.1 to 4.13.7 in /frontend by @dependabot[bot] in #2892
- chore(deps): bump svgo from 2.8.3 to 2.8.4 in /frontend by @dependabot[bot] in #2888
- chore(deps-dev): bump js-yaml from 3.14.2 to 3.15.2 in /frontend by @dependabot[bot] in #2889
- chore(deps): bump sharp and next in /frontend by @dependabot[bot] in #2891
- chore(deps): bump baseline-browser-mapping from 2.10.8 to 2.11.21 in /frontend by @dependabot[bot] in #2890
- chore(deps): bump browserslist from 4.28.1 to 4.28.9 in /frontend by @dependabot[bot] in #2881
- chore(deps): bump fast-uri from 3.1.5 to 3.1.7 in /frontend by @dependabot[bot] in #2880
- chore(deps): bump postcss-selector-parser in /frontend by @dependabot[bot] in #2878
- chore(deps): bump vue from 3.5.40 to 3.5.42 in /frontend by @dependabot[bot] in #2873
- chore(deps): bump vue-router from 5.2.0 to 5.3.1 in /frontend by @dependabot[bot] in #2876
- chore(deps-dev): bump @vitejs/plugin-vue from 6.0.7 to 6.0.8 in /frontend by @dependabot[bot] in #2856
- chore(deps): bump preact from 10.28.4 to 10.29.8 in /frontend by @dependabot[bot] in #2858
- chore(deps): bump golang.org/x/crypto from 0.54.0 to 0.55.0 in /backend by @dependabot[bot] in #2851
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.36 to 1.32.37 in /backend by @dependabot[bot] in #2861
- chore(deps): bump github.com/stretchr/testify from 1.11.1 to 1.12.1 in /backend by @dependabot[bot] in #2862
- chore(deps): bump github.com/labstack/echo/v4 from 4.9.0 to 4.15.3 in /quickstart by @dependabot[bot] in #2866
- chore: autogenerate import JSON schema by @github-actions[bot] in #2886
- feat: static passcode generator for tests by @pvanek in #2793
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.32.37 to 1.33.3 in /backend by @dependabot[bot] in #2904
- chore(deps): bump github.com/go-playground/validator/v10 from 10.30.3 to 10.30.4 in /backend by @dependabot[bot] in #2903
- chore(deps): bump github.com/coreos/go-oidc/v3 from 3.20.0 to 3.21.0 in /backend by @dependabot[bot] in #2902
- chore(deps-dev): bump css-loader from 7.1.4 to 7.1.5 in /frontend by @dependabot[bot] in #2898
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.55.5 to 1.59.0 in /backend by @dependabot[bot] in #2901
- chore(deps-dev): bump @hono/node-server from 1.19.9 to 1.19.17 in /frontend by @dependabot[bot] in #2915
- chore(deps): bump nanoid from 3.3.16 to 3.3.19 in /frontend by @dependabot[bot] in #2917
- chore(deps): bump golang.org/x/crypto from 0.55.0 to 0.57.0 in /backend by @dependabot[bot] in #2909
- chore(deps): bump github.com/beevik/etree from 1.7.0 to 1.8.0 in /backend by @dependabot[bot] in #2910
- chore(deps-dev): bump webpack from 5.109.2 to 5.110.3 in /frontend by @dependabot[bot] in #2897
- chore(deps): bump github.com/gobuffalo/pop/v6 from 6.3.0 to 6.4.0 in /backend by @dependabot[bot] in #2908
- fix(backend): embed version in container images by @ousamabenyounes in #2874
- chore(deps): bump github.com/jackc/pgx/v5 from 5.10.0 to 5.11.0 in /backend by @dependabot[bot] in #2911
- chore(deps): bump golang.org/x/oauth2 from 0.36.0 to 0.37.0 in /backend by @dependabot[bot] in #2925
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.59.0 to 1.60.0 in /backend by @dependabot[bot] in #2924
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.33.3 to 1.33.4 in /backend by @dependabot[bot] in #2922
- chore(deps-dev): bump turbo from 2.10.9 to 2.10.12 in /frontend by @dependabot[bot] in #2927
- feat: tenant scoped public user id by @lfleischmann in #2826
- chore: autogenerate import JSON schema by @github-actions[bot] in #2929
- chore(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.33.4 to 1.33.5 in /backend by @dependabot[bot] in #2932
- chore(deps-dev): bump vue-tsc from 3.3.7 to 3.3.11 in /frontend by @dependabot[bot] in #2930
- chore(deps): bump github.com/aws/aws-sdk-go-v2/service/kms from 1.60.0 to 1.61.0 in /backend by @dependabot[bot] in #2935
- fix: add validation to prevent linking unverified third-party provider emails by @FreddyDevelop in #2885
- fix(saml): avoid nil pointer panic when SAML assertion yields no email by @lfleischmann in #2933
- chore(deps): bump github.com/gobwas/glob from 0.2.3 to 1.0.0 in /backend by @dependabot[bot] in #2919
- chore(deps-dev): bump @vitejs/plugin-react from 5.1.4 to 6.1.1 in /frontend by @dependabot[bot] in #2912
- chore(deps-dev): bump jest-environment-jsdom from 29.7.0 to 30.5.1 in /frontend by @dependabot[bot] in #2914
- chore(deps-dev): bump vite from 7.3.0 to 8.3.0 in /frontend by @dependabot[bot] in #2926
- chore(deps): bump github.com/go-redsync/redsync/v4 from 4.17.0 to 4.18.0 in /backend by @dependabot[bot] in #2937
- chore(deps): bump github.com/gobuffalo/pop/v6 from 6.4.0 to 6.4.1 in /backend by @dependabot[bot] in #2938
- chore(deps-dev): bump jest from 30.3.0 to 30.5.2 in /frontend by @dependabot[bot] in #2936
- chore(deps-dev): bump sass-loader from 16.0.7 to 17.0.1 in /frontend by @dependabot[bot] in #2931
- fix(backend): allow long user agents by @ousamabenyounes in #2882
- feat: tenant-defined custom claims mapped from SAML/OIDC connections by @lfleischmann in #2884
- chore(deps-dev): bump ts-jest from 29.4.11 to 29.4.12 in /frontend by @dependabot[bot] in #2949
- chore(deps-dev): bump turbo from 2.10.12 to 2.11.2 in /frontend by @dependabot[bot] in #2939
- chore(deps): bump dotenv from 17.4.2 to 18.0.1 in /frontend by @dependabot[bot] in #2941
- chore(deps-dev): bump webpack-cli from 7.2.2 to 7.2.3 in /frontend by @dependabot[bot] in #2947
- chore(deps-dev): bump webpack from 5.110.3 to 5.111.1 in /frontend by @dependabot[bot] in #2952
- chore(deps-dev): bump sass from 1.102.0 to 1.104.1 in /frontend by @dependabot[bot] in #2951
- chore(deps): bump github.com/go-playground/validator/v10 from 10.30.4 to 10.30.5 in /backend by @dependabot[bot] in #2950
- chore(deps): bump @rollup/rollup-linux-x64-gnu from 4.60.0 to 4.63.4 in /frontend by @dependabot[bot] in #2948
- chore(deps-dev): bump prettier from 3.8.1 to 3.9.8 in /frontend by @dependabot[bot] in #2958
- chore(deps-dev): bump @tsconfig/node24 from 24.0.4 to 24.0.5 in /frontend by @dependabot[bot] in #2959
- chore(deps): bump react-router-dom from 7.18.2 to 7.18.4 in /frontend by @dependabot[bot] in #2957
- chore(deps): bump vue from 3.5.42 to 3.5.43 in /frontend by @dependabot[bot] in #2964
- chore(deps): bump next from 16.3.4 to 16.3.6 in /frontend by @dependabot[bot] in #2962
- chore(deps): bump dotenv from 18.0.1 to 18.0.3 in /frontend by @dependabot[bot] in #2965
- fix: custom claim description length by @lfleischmann in #2967
- feat(saml): increase default SP certificate validity to 10 years by @lfleischmann in #2980
- feat: organizations and roles by @lfleischmann in #2893
New Contributors
- @moduvoice made their first contribution in #2677
- @glatinone made their first contribution in #2725
- @ousamabenyounes made their first contribution in #2824
- @ftn0 made their first contribution in #2839
- @pvanek made their first contribution in #2793
Full Changelog: backend/v3.0.4...backend/v3.1.0