Full Code of teamhanko/hanko for AI

main 37f475739eea cached
998 files
5.2 MB
1.4M tokens
3009 symbols
1 requests
Download .txt
Showing preview only (5,681K chars total). Download the full file or copy to clipboard to get everything.
Repository: teamhanko/hanko
Branch: main
Commit: 37f475739eea
Files: 998
Total size: 5.2 MB

Directory structure:
gitextract_n6eqdkjy/

├── .editorconfig
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── BUG_REPORT.yml
│   │   ├── FEATURE_REQUEST.yml
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── build-frontend.yml
│       ├── cli-publish.yml
│       ├── codeql-analysis.yml
│       ├── docker-publish.yml
│       ├── e2e.yml
│       ├── go.yml
│       ├── release-frontend-sdk.yml
│       ├── release-hanko-elements.yml
│       ├── schema-generate-config.yml
│       ├── schema-generate-import.yml
│       ├── schema-markdown-config.yml
│       └── schema-markdown-import.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── backend/
│   ├── .goreleaser.yaml
│   ├── Dockerfile
│   ├── Dockerfile.debug
│   ├── README.md
│   ├── audit_log/
│   │   └── logger.go
│   ├── build_info/
│   │   └── build_info.go
│   ├── cmd/
│   │   ├── cleanup/
│   │   │   └── cleanup.go
│   │   ├── isready/
│   │   │   └── isready.go
│   │   ├── jwk/
│   │   │   ├── create.go
│   │   │   └── root.go
│   │   ├── jwt/
│   │   │   ├── create.go
│   │   │   └── root.go
│   │   ├── migrate/
│   │   │   ├── down.go
│   │   │   ├── root.go
│   │   │   └── up.go
│   │   ├── root.go
│   │   ├── schema/
│   │   │   ├── generate.go
│   │   │   ├── generate_config.go
│   │   │   ├── generate_import.go
│   │   │   ├── markdown.go
│   │   │   ├── markdown_config.go
│   │   │   ├── markdown_import.go
│   │   │   └── root.go
│   │   ├── serve/
│   │   │   ├── admin.go
│   │   │   ├── all.go
│   │   │   ├── public.go
│   │   │   └── root.go
│   │   ├── siwa/
│   │   │   └── siwa.go
│   │   ├── user/
│   │   │   ├── export.go
│   │   │   ├── format.go
│   │   │   ├── format_test.go
│   │   │   ├── generate.go
│   │   │   ├── import.go
│   │   │   ├── import_test.go
│   │   │   ├── importer.go
│   │   │   └── root.go
│   │   └── version/
│   │       └── version.go
│   ├── config/
│   │   ├── config.go
│   │   ├── config.yaml
│   │   ├── config_account.go
│   │   ├── config_audit_log.go
│   │   ├── config_database.go
│   │   ├── config_default.go
│   │   ├── config_email.go
│   │   ├── config_email_delivery.go
│   │   ├── config_emails.go
│   │   ├── config_flow_locker.go
│   │   ├── config_logger.go
│   │   ├── config_mfa.go
│   │   ├── config_passcode.go
│   │   ├── config_passkey.go
│   │   ├── config_password.go
│   │   ├── config_privacy.go
│   │   ├── config_rate_limiter.go
│   │   ├── config_secrets.go
│   │   ├── config_security_notifications.go
│   │   ├── config_server.go
│   │   ├── config_service.go
│   │   ├── config_session.go
│   │   ├── config_shared.go
│   │   ├── config_test.go
│   │   ├── config_third_party.go
│   │   ├── config_username.go
│   │   ├── config_webauthn.go
│   │   ├── config_webhook.go
│   │   ├── config_webhook_test.go
│   │   ├── minimal-config.yaml
│   │   ├── passcode-smtp-config.yaml
│   │   ├── root-passcode-smtp-config.yaml
│   │   └── security-notifications-disabled-config.yaml
│   ├── crypto/
│   │   ├── aes_gcm/
│   │   │   ├── aes_gcm.go
│   │   │   └── aes_gcm_test.go
│   │   ├── jwk/
│   │   │   ├── aws_kms/
│   │   │   │   ├── adapter.go
│   │   │   │   └── manager.go
│   │   │   ├── local_db/
│   │   │   │   ├── generator.go
│   │   │   │   ├── generator_rsa.go
│   │   │   │   ├── generator_test.go
│   │   │   │   ├── manager.go
│   │   │   │   └── manager_test.go
│   │   │   ├── manager.go
│   │   │   └── types.go
│   │   ├── passcode.go
│   │   ├── passcode_test.go
│   │   └── string.go
│   ├── dto/
│   │   ├── admin/
│   │   │   ├── email.go
│   │   │   ├── identity.go
│   │   │   ├── metadata.go
│   │   │   ├── otp.go
│   │   │   ├── password.go
│   │   │   ├── session.go
│   │   │   ├── user.go
│   │   │   ├── username.go
│   │   │   ├── webauthn.go
│   │   │   └── webhook.go
│   │   ├── config.go
│   │   ├── email.go
│   │   ├── error_handler.go
│   │   ├── intern/
│   │   │   ├── WebauthnCredential.go
│   │   │   ├── WebauthnSessionData.go
│   │   │   └── WebauthnUser.go
│   │   ├── metadata.go
│   │   ├── passcode.go
│   │   ├── profile.go
│   │   ├── session.go
│   │   ├── session_test.go
│   │   ├── thirdparty.go
│   │   ├── user.go
│   │   ├── username.go
│   │   ├── validator.go
│   │   ├── webauthn.go
│   │   └── webhook/
│   │       └── email.go
│   ├── ee/
│   │   └── saml/
│   │       ├── config/
│   │       │   ├── saml.go
│   │       │   └── saml_test.go
│   │       ├── dto/
│   │       │   └── saml.go
│   │       ├── handler.go
│   │       ├── provider/
│   │       │   ├── auth0.go
│   │       │   ├── provider.go
│   │       │   └── saml.go
│   │       ├── router.go
│   │       ├── service.go
│   │       ├── state.go
│   │       ├── state_test.go
│   │       └── utils/
│   │           ├── response.go
│   │           └── url.go
│   ├── flow_api/
│   │   ├── flow/
│   │   │   ├── capabilities/
│   │   │   │   └── action_send_capabilities.go
│   │   │   ├── credential_onboarding/
│   │   │   │   ├── action_continue_to_passkey.go
│   │   │   │   ├── action_continue_to_password.go
│   │   │   │   ├── action_register_password.go
│   │   │   │   ├── action_skip_method_chooser.go
│   │   │   │   ├── action_skip_passkey.go
│   │   │   │   ├── action_skip_password.go
│   │   │   │   ├── action_webauthn_generate_creation_options.go
│   │   │   │   └── action_webauthn_verify_attestation_response.go
│   │   │   ├── credential_usage/
│   │   │   │   ├── action_continue_to_passcode_confirmation.go
│   │   │   │   ├── action_continue_to_passcode_confirmation_recovery.go
│   │   │   │   ├── action_continue_to_password_login.go
│   │   │   │   ├── action_continue_with_login_identifier.go
│   │   │   │   ├── action_password_login.go
│   │   │   │   ├── action_password_recovery.go
│   │   │   │   ├── action_remember_me.go
│   │   │   │   ├── action_resend_passcode.go
│   │   │   │   ├── action_verify_passcode.go
│   │   │   │   ├── action_webauthn_generate_request_options.go
│   │   │   │   ├── action_webauthn_verify_assertion_response.go
│   │   │   │   └── hook_send_passcode.go
│   │   │   ├── device_trust/
│   │   │   │   ├── action_trust_device.go
│   │   │   │   ├── hook_issue_trust_device_cookie.go
│   │   │   │   └── hook_schedule_trust_device_state.go
│   │   │   ├── flows.go
│   │   │   ├── login/
│   │   │   │   ├── hook_create_email.go
│   │   │   │   ├── hook_schedule_onboarding_states.go
│   │   │   │   ├── hook_trigger_login_webhook.go
│   │   │   │   └── hook_webauthn_generate_request_options_cond.go
│   │   │   ├── mfa_creation/
│   │   │   │   ├── action_continue_to_otp_secret_creation.go
│   │   │   │   ├── action_continue_to_security_key_creation.go
│   │   │   │   ├── action_otp_code_verify.go
│   │   │   │   ├── action_webauthn_generate_creation_options_for_security_keys.go
│   │   │   │   ├── hook_otp_secret_generate.go
│   │   │   │   └── skip_mfa.go
│   │   │   ├── mfa_usage/
│   │   │   │   ├── action_continue_to_login_otp.go
│   │   │   │   ├── action_continue_to_login_security_key.go
│   │   │   │   ├── action_otp_code_validate.go
│   │   │   │   └── action_webauthn_generate_request_options_security_key.go
│   │   │   ├── profile/
│   │   │   │   ├── action_account_delete.go
│   │   │   │   ├── action_connect_thirdparty_oauth_provider.go
│   │   │   │   ├── action_continue_to_otp_secret_creation.go
│   │   │   │   ├── action_continue_to_security_key_creation.go
│   │   │   │   ├── action_disconnect_thirdparty_oauth_provider.go
│   │   │   │   ├── action_email_create.go
│   │   │   │   ├── action_email_delete.go
│   │   │   │   ├── action_email_set_primary.go
│   │   │   │   ├── action_email_verify.go
│   │   │   │   ├── action_exchange_token.go
│   │   │   │   ├── action_otp_secret_delete.go
│   │   │   │   ├── action_password_create.go
│   │   │   │   ├── action_password_delete.go
│   │   │   │   ├── action_password_update.go
│   │   │   │   ├── action_patch_metadata.go
│   │   │   │   ├── action_security_key_create.go
│   │   │   │   ├── action_security_key_delete.go
│   │   │   │   ├── action_session_delete.go
│   │   │   │   ├── action_username_create.go
│   │   │   │   ├── action_username_delete.go
│   │   │   │   ├── action_username_update.go
│   │   │   │   ├── action_webauthn_credential_create.go
│   │   │   │   ├── action_webauthn_credential_delete.go
│   │   │   │   ├── action_webauthn_credential_rename.go
│   │   │   │   ├── action_webauthn_verify_attestation_response.go
│   │   │   │   ├── hook_get_profile_data.go
│   │   │   │   ├── hook_get_sessions.go
│   │   │   │   └── hook_refresh_session_user.go
│   │   │   ├── registration/
│   │   │   │   ├── action_register_login_identifier.go
│   │   │   │   ├── hook_create_user.go
│   │   │   │   └── hook_determine_amr_values.go
│   │   │   ├── shared/
│   │   │   │   ├── action_back.go
│   │   │   │   ├── action_exchange_token.go
│   │   │   │   ├── action_skip.go
│   │   │   │   ├── action_thirdparty_oauth.go
│   │   │   │   ├── const_action_names.go
│   │   │   │   ├── const_email_templates.go
│   │   │   │   ├── const_flow_names.go
│   │   │   │   ├── const_stash_paths.go
│   │   │   │   ├── const_state_names.go
│   │   │   │   ├── errors.go
│   │   │   │   ├── flow.go
│   │   │   │   ├── hook_determine_amr_values.go
│   │   │   │   ├── hook_email_persist_verified_status.go
│   │   │   │   ├── hook_generate_oauth_links.go
│   │   │   │   ├── hook_get_user_data.go
│   │   │   │   ├── hook_issue_session.go
│   │   │   │   ├── hook_password_save.go
│   │   │   │   ├── hook_persist_webauthn_credential.go
│   │   │   │   ├── hook_schedule_mfa_creation_states.go
│   │   │   │   ├── hook_verify_attestation_response.go
│   │   │   │   └── links.go
│   │   │   └── user_details/
│   │   │       ├── action_set_email.go
│   │   │       ├── action_set_username.go
│   │   │       ├── action_skip_email.go
│   │   │       └── action_skip_username.go
│   │   ├── flow_locker/
│   │   │   ├── flow_locker.go
│   │   │   ├── flow_locker_test.go
│   │   │   ├── memory.go
│   │   │   ├── memory_test.go
│   │   │   ├── noop.go
│   │   │   ├── redis.go
│   │   │   └── redis_test.go
│   │   ├── handler.go
│   │   ├── services/
│   │   │   ├── device_trust.go
│   │   │   ├── email.go
│   │   │   ├── passcode.go
│   │   │   ├── password.go
│   │   │   ├── security_notification.go
│   │   │   ├── user.go
│   │   │   └── webauthn.go
│   │   └── static/
│   │       └── generic_client.html
│   ├── flowpilot/
│   │   ├── action_input.go
│   │   ├── builder.go
│   │   ├── builder_subflow.go
│   │   ├── context.go
│   │   ├── context_action_exec.go
│   │   ├── context_action_init.go
│   │   ├── context_flow.go
│   │   ├── db.go
│   │   ├── errors.go
│   │   ├── flow.go
│   │   ├── input.go
│   │   ├── input_allowed_value.go
│   │   ├── input_schema.go
│   │   ├── jsonmanager/
│   │   │   └── manager.go
│   │   ├── link.go
│   │   ├── payload.go
│   │   ├── query_param.go
│   │   ├── random.go
│   │   ├── response.go
│   │   ├── stash.go
│   │   ├── state_action.go
│   │   └── state_detail.go
│   ├── go.mod
│   ├── go.sum
│   ├── handler/
│   │   ├── admin_router.go
│   │   ├── audit_log.go
│   │   ├── email.go
│   │   ├── email_admin.go
│   │   ├── email_admin_test.go
│   │   ├── email_test.go
│   │   ├── health.go
│   │   ├── health_test.go
│   │   ├── helpers_test.go
│   │   ├── metadata_admin.go
│   │   ├── metadata_admin_test.go
│   │   ├── otp_admin.go
│   │   ├── otp_admin_test.go
│   │   ├── passcode.go
│   │   ├── passcode_test.go
│   │   ├── password.go
│   │   ├── password_admin.go
│   │   ├── password_admin_test.go
│   │   ├── password_test.go
│   │   ├── public_router.go
│   │   ├── session.go
│   │   ├── session_admin.go
│   │   ├── session_admin_test.go
│   │   ├── status.go
│   │   ├── thirdparty.go
│   │   ├── thirdparty_auth_test.go
│   │   ├── thirdparty_callback_error_test.go
│   │   ├── thirdparty_callback_test.go
│   │   ├── thirdparty_test.go
│   │   ├── token.go
│   │   ├── token_test.go
│   │   ├── user.go
│   │   ├── user_admin.go
│   │   ├── user_admin_test.go
│   │   ├── user_test.go
│   │   ├── utils.go
│   │   ├── webauthn.go
│   │   ├── webauthn_credential_admin.go
│   │   ├── webauthn_credential_admin_test.go
│   │   ├── webauthn_test.go
│   │   ├── webhook.go
│   │   ├── webhook_test.go
│   │   ├── well_known.go
│   │   └── well_known_test.go
│   ├── json_schema/
│   │   ├── hanko.config.json
│   │   └── hanko.user_import.json
│   ├── mail/
│   │   ├── locales/
│   │   │   ├── passcode.bn.yaml
│   │   │   ├── passcode.de.yaml
│   │   │   ├── passcode.en.yaml
│   │   │   ├── passcode.fr.yaml
│   │   │   ├── passcode.it.yaml
│   │   │   ├── passcode.nl.yaml
│   │   │   ├── passcode.pt-BR.yaml
│   │   │   ├── passcode.zh-CN.yaml
│   │   │   ├── security-notifications.bn.yaml
│   │   │   ├── security-notifications.de.yaml
│   │   │   ├── security-notifications.en.yaml
│   │   │   ├── security-notifications.fr.yaml
│   │   │   ├── security-notifications.it.yaml
│   │   │   ├── security-notifications.nl.yaml
│   │   │   ├── security-notifications.pt-BR.yaml
│   │   │   └── security-notifications.zh-CN.yaml
│   │   ├── mailer.go
│   │   ├── mailer_test.go
│   │   ├── render.go
│   │   ├── render_test.go
│   │   └── templates/
│   │       ├── email_create.html.tmpl
│   │       ├── email_create.txt.tmpl
│   │       ├── email_delete.html.tmpl
│   │       ├── email_delete.txt.tmpl
│   │       ├── email_login_attempted.html.tmpl
│   │       ├── email_login_attempted.txt.tmpl
│   │       ├── email_registration_attempted.html.tmpl
│   │       ├── email_registration_attempted.txt.tmpl
│   │       ├── email_verification.html.tmpl
│   │       ├── email_verification.txt.tmpl
│   │       ├── layout.html.tmpl
│   │       ├── login.html.tmpl
│   │       ├── login.txt.tmpl
│   │       ├── mfa_create.html.tmpl
│   │       ├── mfa_create.txt.tmpl
│   │       ├── mfa_delete.html.tmpl
│   │       ├── mfa_delete.txt.tmpl
│   │       ├── passkey_create.html.tmpl
│   │       ├── passkey_create.txt.tmpl
│   │       ├── password_update.html.tmpl
│   │       ├── password_update.txt.tmpl
│   │       ├── primary_email_update.html.tmpl
│   │       ├── primary_email_update.txt.tmpl
│   │       ├── recovery.html.tmpl
│   │       └── recovery.txt.tmpl
│   ├── main.go
│   ├── mapper/
│   │   ├── aaguid.json
│   │   └── authenticator_mapper.go
│   ├── middleware/
│   │   ├── logger.go
│   │   ├── session.go
│   │   └── webhook.go
│   ├── pagination/
│   │   ├── header.go
│   │   └── header_test.go
│   ├── persistence/
│   │   ├── audit_log_persister.go
│   │   ├── email_persister.go
│   │   ├── flow_persister.go
│   │   ├── identity_persister.go
│   │   ├── jwk_persister.go
│   │   ├── migrations/
│   │   │   ├── 20220405153240_create_users.down.fizz
│   │   │   ├── 20220405153240_create_users.up.fizz
│   │   │   ├── 20220405153750_create_passcodes.down.fizz
│   │   │   ├── 20220405153750_create_passcodes.up.fizz
│   │   │   ├── 20220405154240_create_webauthn_credentials.down.fizz
│   │   │   ├── 20220405154240_create_webauthn_credentials.up.fizz
│   │   │   ├── 20220405154750_create_webauthn_session_data.down.fizz
│   │   │   ├── 20220405154750_create_webauthn_session_data.up.fizz
│   │   │   ├── 20220405155120_create_webauthn_session_data_allowed_credentials.down.fizz
│   │   │   ├── 20220405155120_create_webauthn_session_data_allowed_credentials.up.fizz
│   │   │   ├── 20220413152500_create_jwk.down.fizz
│   │   │   ├── 20220413152500_create_jwk.up.fizz
│   │   │   ├── 20220425122015_create_password_credentials.down.fizz
│   │   │   ├── 20220425122015_create_password_credentials.up.fizz
│   │   │   ├── 20220711121022_create_credential_transports.down.fizz
│   │   │   ├── 20220711121022_create_credential_transports.up.fizz
│   │   │   ├── 20220818111000_create_audit_logs.down.fizz
│   │   │   ├── 20220818111000_create_audit_logs.up.fizz
│   │   │   ├── 20221027104800_create_emails.down.fizz
│   │   │   ├── 20221027104800_create_emails.up.fizz
│   │   │   ├── 20221027104900_change_users.down.fizz
│   │   │   ├── 20221027104900_change_users.up.fizz
│   │   │   ├── 20221027123530_change_passcodes.down.fizz
│   │   │   ├── 20221027123530_change_passcodes.up.fizz
│   │   │   ├── 20221222134900_change_webauthn_credentials.down.fizz
│   │   │   ├── 20221222134900_change_webauthn_credentials.up.fizz
│   │   │   ├── 20230112152816_create_identities.down.fizz
│   │   │   ├── 20230112152816_create_identities.up.fizz
│   │   │   ├── 20230206102000_change_webauthn_credentials.down.fizz
│   │   │   ├── 20230206102000_change_webauthn_credentials.up.fizz
│   │   │   ├── 20230222114100_change_webauthn_credentials.down.fizz
│   │   │   ├── 20230222114100_change_webauthn_credentials.up.fizz
│   │   │   ├── 20230317114334_create_tokens.down.fizz
│   │   │   ├── 20230317114334_create_tokens.up.fizz
│   │   │   ├── 20230801124808_webauthn_session_data_add_expiry.down.fizz
│   │   │   ├── 20230801124808_webauthn_session_data_add_expiry.up.fizz
│   │   │   ├── 20230810173315_create_flows.down.fizz
│   │   │   ├── 20230810173315_create_flows.up.fizz
│   │   │   ├── 20230905102601_create_saml_state.down.fizz
│   │   │   ├── 20230905102601_create_saml_state.up.fizz
│   │   │   ├── 20230915111552_create_saml_certs.down.fizz
│   │   │   ├── 20230915111552_create_saml_certs.up.fizz
│   │   │   ├── 20231012141100_change_user_table.down.fizz
│   │   │   ├── 20231012141100_change_user_table.up.fizz
│   │   │   ├── 20231013113800_change_passcode_table.down.fizz
│   │   │   ├── 20231013113800_change_passcode_table.up.fizz
│   │   │   ├── 20240108094151_create_webhooks.down.fizz
│   │   │   ├── 20240108094151_create_webhooks.up.fizz
│   │   │   ├── 20240108094210_create_webhook_events.down.fizz
│   │   │   ├── 20240108094210_create_webhook_events.up.fizz
│   │   │   ├── 20240207150616_change_audit_logs.down.fizz
│   │   │   ├── 20240207150616_change_audit_logs.up.fizz
│   │   │   ├── 20240530122100_change_tokens.down.fizz
│   │   │   ├── 20240530122100_change_tokens.up.fizz
│   │   │   ├── 20240530145724_change_users.down.fizz
│   │   │   ├── 20240530145724_change_users.up.fizz
│   │   │   ├── 20240612122326_change_flows.down.fizz
│   │   │   ├── 20240612122326_change_flows.up.fizz
│   │   │   ├── 20240717020131_drop_transitions.down.fizz
│   │   │   ├── 20240717020131_drop_transitions.up.fizz
│   │   │   ├── 20240717020707_change_flows.down.fizz
│   │   │   ├── 20240717020707_change_flows.up.fizz
│   │   │   ├── 20240723171257_change_passcodes.down.fizz
│   │   │   ├── 20240723171257_change_passcodes.up.fizz
│   │   │   ├── 20240723173648_create_usernames.down.fizz
│   │   │   ├── 20240723173648_create_usernames.up.fizz
│   │   │   ├── 20240826132046_create_otp_secrets.down.fizz
│   │   │   ├── 20240826132046_create_otp_secrets.up.fizz
│   │   │   ├── 20240826133417_change_webauthn_credentials.down.fizz
│   │   │   ├── 20240826133417_change_webauthn_credentials.up.fizz
│   │   │   ├── 20241002113000_create_sessions.down.fizz
│   │   │   ├── 20241002113000_create_sessions.up.fizz
│   │   │   ├── 20241106171500_change_sessions.down.fizz
│   │   │   ├── 20241106171500_change_sessions.up.fizz
│   │   │   ├── 20241112181011_create_trusted_devices.down.fizz
│   │   │   ├── 20241112181011_create_trusted_devices.up.fizz
│   │   │   ├── 20241118114500_change_webauthn_credentials.down.fizz
│   │   │   ├── 20241118114500_change_webauthn_credentials.up.fizz
│   │   │   ├── 20250130154010_change_identities.down.fizz
│   │   │   ├── 20250130154010_change_identities.up.fizz
│   │   │   ├── 20250130170131_create_saml_identities.down.fizz
│   │   │   ├── 20250130170131_create_saml_identities.up.fizz
│   │   │   ├── 20250210095906_create_saml_idp_initiated_requests.down.fizz
│   │   │   ├── 20250210095906_create_saml_idp_initiated_requests.up.fizz
│   │   │   ├── 20250313160348_change_flows.down.fizz
│   │   │   ├── 20250313160348_change_flows.up.fizz
│   │   │   ├── 20250414165334_create_user_metadata.down.fizz
│   │   │   ├── 20250414165334_create_user_metadata.up.fizz
│   │   │   ├── 20251002113000_change_tokens.down.fizz
│   │   │   ├── 20251002113000_change_tokens.up.fizz
│   │   │   ├── 20251104000000_add_user_id_to_identities.down.fizz
│   │   │   ├── 20251104000000_add_user_id_to_identities.up.fizz
│   │   │   ├── 20251119000000_change_tokens.down.fizz
│   │   │   ├── 20251119000000_change_tokens.up.fizz
│   │   │   ├── 20260204151021_change_user.down.fizz
│   │   │   └── 20260204151021_change_user.up.fizz
│   │   ├── models/
│   │   │   ├── audit_log.go
│   │   │   ├── email.go
│   │   │   ├── flow.go
│   │   │   ├── identity.go
│   │   │   ├── jwk.go
│   │   │   ├── otp_secret.go
│   │   │   ├── passcode.go
│   │   │   ├── password_credential.go
│   │   │   ├── primary_email.go
│   │   │   ├── saml_certificate.go
│   │   │   ├── saml_identity.go
│   │   │   ├── saml_idp_initiated_request.go
│   │   │   ├── saml_state.go
│   │   │   ├── session.go
│   │   │   ├── token.go
│   │   │   ├── trusted_device.go
│   │   │   ├── user.go
│   │   │   ├── user_metadata.go
│   │   │   ├── username.go
│   │   │   ├── webauthn_credential.go
│   │   │   ├── webauthn_credential_transport.go
│   │   │   ├── webauthn_credential_user_handle.go
│   │   │   ├── webauthn_session_data.go
│   │   │   ├── webauthn_session_data_allowed_credential.go
│   │   │   ├── webhook.go
│   │   │   └── webhook_event.go
│   │   ├── otp_secret_persister.go
│   │   ├── passcode_persister.go
│   │   ├── password_credential_persister.go
│   │   ├── persister.go
│   │   ├── primary_email_persister.go
│   │   ├── saml_certificate_persister.go
│   │   ├── saml_identity_persister.go
│   │   ├── saml_idp_inititated_request_persister.go
│   │   ├── saml_state_persister.go
│   │   ├── session_persister.go
│   │   ├── token_persister.go
│   │   ├── trusted_device_persister.go
│   │   ├── user_metadata_persister.go
│   │   ├── user_persister.go
│   │   ├── username_persister.go
│   │   ├── webauthn_credential_persister.go
│   │   ├── webauthn_credential_user_handle_persister.go
│   │   ├── webauthn_session_data_persister.go
│   │   └── webhook_persister.go
│   ├── rate_limiter/
│   │   ├── rate_limiter.go
│   │   └── rate_limiter_test.go
│   ├── server/
│   │   └── server.go
│   ├── session/
│   │   ├── session.go
│   │   ├── session_test.go
│   │   ├── template.go
│   │   └── template_test.go
│   ├── template/
│   │   ├── template.go
│   │   └── templates/
│   │       └── status.tmpl
│   ├── test/
│   │   ├── audit_logger.go
│   │   ├── config.go
│   │   ├── database.go
│   │   ├── fixtures/
│   │   │   ├── actions/
│   │   │   │   ├── get_wa_creation_options/
│   │   │   │   │   └── flows.yaml
│   │   │   │   ├── send_capabilities/
│   │   │   │   │   └── flows.yaml
│   │   │   │   ├── send_wa_attestation_response/
│   │   │   │   │   ├── flows.yaml
│   │   │   │   │   └── webauthn_session_data.yaml
│   │   │   │   ├── submit_new_password/
│   │   │   │   │   └── flows.yaml
│   │   │   │   ├── submit_passcode/
│   │   │   │   │   ├── flows.yaml
│   │   │   │   │   └── passcodes.yaml
│   │   │   │   └── submit_registration_identifier/
│   │   │   │       ├── emails.yaml
│   │   │   │       ├── flows.yaml
│   │   │   │       └── users.yaml
│   │   │   ├── email/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── metadata/
│   │   │   │   ├── user_metadata.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── otp/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── otp_secrets.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── passcode/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── password/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── password_credentials.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── saml_state/
│   │   │   │   └── saml_states.yaml
│   │   │   ├── sessions/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   ├── sessions.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── thirdparty/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── identities.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── token/
│   │   │   │   ├── tokens.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── user/
│   │   │   │   ├── emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── user_admin/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── usernames.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── user_with_webauthn_credential/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── identities.yaml
│   │   │   │   ├── user_metadata.yaml
│   │   │   │   ├── usernames.yaml
│   │   │   │   ├── users.yaml
│   │   │   │   └── webauthn_credentials.yaml
│   │   │   ├── webauthn/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   ├── users.yaml
│   │   │   │   ├── webauthn_credentials.yaml
│   │   │   │   └── webauthn_session_data.yaml
│   │   │   ├── webauthn_registration/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   ├── users.yaml
│   │   │   │   ├── webauthn_credentials.yaml
│   │   │   │   └── webauthn_session_data.yaml
│   │   │   └── webhooks/
│   │   │       ├── webhook_events.yaml
│   │   │       └── webhooks.yaml
│   │   ├── jwk_manager.go
│   │   ├── mailslurper.go
│   │   └── suite.go
│   ├── thirdparty/
│   │   ├── claims.go
│   │   ├── error.go
│   │   ├── helper.go
│   │   ├── helper_test.go
│   │   ├── linking.go
│   │   ├── provider.go
│   │   ├── provider_apple.go
│   │   ├── provider_custom.go
│   │   ├── provider_discord.go
│   │   ├── provider_facebook.go
│   │   ├── provider_github.go
│   │   ├── provider_google.go
│   │   ├── provider_linkedin.go
│   │   ├── provider_microsoft.go
│   │   ├── state.go
│   │   └── state_test.go
│   ├── utils/
│   │   ├── cookie.go
│   │   ├── cookie_test.go
│   │   ├── mask.go
│   │   ├── mask_test.go
│   │   ├── url.go
│   │   └── url_test.go
│   └── webhooks/
│       ├── config_hook.go
│       ├── config_hook_test.go
│       ├── database_hook.go
│       ├── database_hook_test.go
│       ├── events/
│       │   └── events.go
│       ├── manager.go
│       ├── manager_test.go
│       ├── utils/
│       │   ├── webhook.go
│       │   └── webhook_test.go
│       ├── webhook.go
│       ├── webhook_test.go
│       ├── worker.go
│       └── worker_test.go
├── deploy/
│   ├── docker-compose/
│   │   ├── base.yaml
│   │   ├── config-disable-signup.yaml
│   │   ├── config-rate-limiting.yaml
│   │   ├── config.yaml
│   │   ├── quickstart-with-redis.yaml
│   │   ├── quickstart.debug.yaml
│   │   ├── quickstart.e2e.yaml
│   │   ├── quickstart.yaml
│   │   ├── todo-angular.yaml
│   │   ├── todo-nextjs.yaml
│   │   ├── todo-react.yaml
│   │   ├── todo-svelte.yaml
│   │   └── todo-vue.yaml
│   └── k8s/
│       ├── README.md
│       ├── base/
│       │   ├── elements/
│       │   │   ├── deployment.yaml
│       │   │   ├── ingress.yaml
│       │   │   ├── kustomization.yaml
│       │   │   └── service.yaml
│       │   ├── mailhog/
│       │   │   ├── deployment.yaml
│       │   │   ├── ingress.yaml
│       │   │   ├── kustomization.yaml
│       │   │   └── service.yaml
│       │   ├── postgres/
│       │   │   ├── deployment.yaml
│       │   │   ├── initdbscript.sh
│       │   │   ├── kustomization.yaml
│       │   │   ├── persistent-volume.yaml
│       │   │   └── service.yaml
│       │   └── quickstart/
│       │       ├── deployment.yaml
│       │       ├── ingress.yaml
│       │       ├── kustomization.yaml
│       │       └── service.yaml
│       └── overlays/
│           ├── quickstart/
│           │   └── kustomization.yaml
│           └── thirdparty-x-domain/
│               ├── README.md
│               ├── config.yaml
│               ├── env-patch.yaml
│               ├── ingress-patch.yaml
│               └── kustomization.yaml
├── e2e/
│   ├── .eslintignore
│   ├── .eslintrc.cjs
│   ├── .nvmrc
│   ├── README.md
│   ├── fixtures/
│   │   └── Pages.ts
│   ├── global.d.ts
│   ├── helper/
│   │   ├── Accounts.ts
│   │   ├── Endpoints.ts
│   │   ├── MailSlurper.ts
│   │   ├── Matchers.ts
│   │   └── Setup.ts
│   ├── package.json
│   ├── pages/
│   │   ├── BasePage.ts
│   │   ├── Error.ts
│   │   ├── LoginEmail.ts
│   │   ├── LoginEmailNoSignUp.ts
│   │   ├── LoginPasscode.ts
│   │   ├── LoginPassword.ts
│   │   ├── NoAccountFound.ts
│   │   ├── RegisterAuthenticator.ts
│   │   ├── RegisterConfirm.ts
│   │   ├── RegisterPassword.ts
│   │   └── SecuredContent.ts
│   ├── playwright.config.ts
│   ├── seed/
│   │   ├── Dockerfile
│   │   ├── init.sh
│   │   └── seed.sql
│   ├── tests/
│   │   ├── common.spec.ts
│   │   ├── nosignup.spec.ts
│   │   ├── passwordless.spec.ts
│   │   └── passwords.spec.ts
│   └── tsconfig.json
├── frontend/
│   ├── .dockerignore
│   ├── Dockerfile
│   ├── Dockerfile.debug
│   ├── elements/
│   │   ├── .dockerignore
│   │   ├── .eslintignore
│   │   ├── .eslintrc.cjs
│   │   ├── .nvmrc
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── babel.config.cjs
│   │   ├── example.css
│   │   ├── nginx/
│   │   │   └── default.conf
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── Elements.tsx
│   │   │   ├── _mixins.sass
│   │   │   ├── _preset.sass
│   │   │   ├── _variables.sass
│   │   │   ├── components/
│   │   │   │   ├── accordion/
│   │   │   │   │   ├── Accordion.tsx
│   │   │   │   │   ├── AddEmailDropdown.tsx
│   │   │   │   │   ├── AddWebauthnCredentialDropdown.tsx
│   │   │   │   │   ├── ChangePasswordDropdown.tsx
│   │   │   │   │   ├── ChangeUsernameDropdown.tsx
│   │   │   │   │   ├── ConnectIdentityDropdown.tsx
│   │   │   │   │   ├── Dropdown.tsx
│   │   │   │   │   ├── ListEmailsAccordion.tsx
│   │   │   │   │   ├── ListIdentities.tsx
│   │   │   │   │   ├── ListSessionsAccordion.tsx
│   │   │   │   │   ├── ListWebauthnCredentialsAccordion.tsx
│   │   │   │   │   ├── ManageAuthAppDropdown.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── error/
│   │   │   │   │   ├── ErrorBox.tsx
│   │   │   │   │   ├── ErrorMessage.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── form/
│   │   │   │   │   ├── Button.tsx
│   │   │   │   │   ├── Checkbox.tsx
│   │   │   │   │   ├── CodeInput.tsx
│   │   │   │   │   ├── Form.tsx
│   │   │   │   │   ├── Input.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── headline/
│   │   │   │   │   ├── Headline1.tsx
│   │   │   │   │   ├── Headline2.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── icons/
│   │   │   │   │   ├── Apple.tsx
│   │   │   │   │   ├── Checkmark.tsx
│   │   │   │   │   ├── Copy.tsx
│   │   │   │   │   ├── CustomProvider.tsx
│   │   │   │   │   ├── Discord.tsx
│   │   │   │   │   ├── ExclamationMark.tsx
│   │   │   │   │   ├── Facebook.tsx
│   │   │   │   │   ├── GitHub.tsx
│   │   │   │   │   ├── Google.tsx
│   │   │   │   │   ├── Icon.tsx
│   │   │   │   │   ├── LinkedIn.tsx
│   │   │   │   │   ├── LoadingSpinner.tsx
│   │   │   │   │   ├── Mail.tsx
│   │   │   │   │   ├── Microsoft.tsx
│   │   │   │   │   ├── Passkey.tsx
│   │   │   │   │   ├── Password.tsx
│   │   │   │   │   ├── QRCodeScanner.tsx
│   │   │   │   │   ├── SecurityKey.tsx
│   │   │   │   │   ├── Spinner.tsx
│   │   │   │   │   ├── icons.ts
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── link/
│   │   │   │   │   ├── Link.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── otp/
│   │   │   │   │   ├── OTPCreationDetails.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── paragraph/
│   │   │   │   │   ├── Paragraph.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── spacer/
│   │   │   │   │   ├── Divider.tsx
│   │   │   │   │   ├── Spacer.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   └── wrapper/
│   │   │   │       ├── Clipboard.tsx
│   │   │   │       ├── Container.tsx
│   │   │   │       ├── Content.tsx
│   │   │   │       ├── Footer.tsx
│   │   │   │       └── styles.sass
│   │   │   ├── contexts/
│   │   │   │   └── AppProvider.tsx
│   │   │   ├── declarations.d.ts
│   │   │   ├── example.html
│   │   │   ├── hooks/
│   │   │   │   ├── UseFlowEffects.ts
│   │   │   │   └── UseFlowState.ts
│   │   │   ├── i18n/
│   │   │   │   ├── all.ts
│   │   │   │   ├── bn.ts
│   │   │   │   ├── de.ts
│   │   │   │   ├── en.ts
│   │   │   │   ├── fr.ts
│   │   │   │   ├── it.ts
│   │   │   │   ├── nl.ts
│   │   │   │   ├── pt-BR.ts
│   │   │   │   ├── translations.ts
│   │   │   │   └── zh.ts
│   │   │   ├── index.ts
│   │   │   └── pages/
│   │   │       ├── CreateEmailPage.tsx
│   │   │       ├── CreateOTPSecretPage.tsx
│   │   │       ├── CreatePasswordPage.tsx
│   │   │       ├── CreateSecurityKeyPage.tsx
│   │   │       ├── CreateUsernamePage.tsx
│   │   │       ├── CredentialOnboardingChooser.tsx
│   │   │       ├── DeleteAccountPage.tsx
│   │   │       ├── DeviceTrustPage.tsx
│   │   │       ├── EditPasswordPage.tsx
│   │   │       ├── ErrorPage.tsx
│   │   │       ├── InitPage.tsx
│   │   │       ├── LoginInitPage.tsx
│   │   │       ├── LoginMethodChooser.tsx
│   │   │       ├── LoginOTPPage.tsx
│   │   │       ├── LoginPasswordPage.tsx
│   │   │       ├── LoginSecurityKeyPage.tsx
│   │   │       ├── MFAMethodChooserPage.tsx
│   │   │       ├── PasscodePage.tsx
│   │   │       ├── ProfilePage.tsx
│   │   │       ├── RegisterPasskeyPage.tsx
│   │   │       ├── RegistrationInitPage.tsx
│   │   │       └── RenameWebauthnCredentialPage.tsx
│   │   ├── tsconfig.json
│   │   ├── webpack.config.cjs
│   │   └── webpack.config.dev.cjs
│   ├── examples/
│   │   ├── README.md
│   │   ├── angular/
│   │   │   ├── .browserslistrc
│   │   │   ├── .dockerignore
│   │   │   ├── .editorconfig
│   │   │   ├── .gitignore
│   │   │   ├── Dockerfile
│   │   │   ├── README.md
│   │   │   ├── angular.json
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── app/
│   │   │   │   │   ├── app-routing.module.ts
│   │   │   │   │   ├── app.component.css
│   │   │   │   │   ├── app.component.html
│   │   │   │   │   ├── app.component.ts
│   │   │   │   │   ├── login/
│   │   │   │   │   │   ├── login.component.html
│   │   │   │   │   │   └── login.component.ts
│   │   │   │   │   ├── modal/
│   │   │   │   │   │   ├── session-expired-modal.component.html
│   │   │   │   │   │   └── session-expired-modal.component.ts
│   │   │   │   │   ├── profile/
│   │   │   │   │   │   ├── profile.component.html
│   │   │   │   │   │   └── profile.component.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   ├── hanko.services.ts
│   │   │   │   │   │   └── todo.service.ts
│   │   │   │   │   └── todo/
│   │   │   │   │       ├── todo.component.css
│   │   │   │   │       ├── todo.component.html
│   │   │   │   │       └── todo.component.ts
│   │   │   │   ├── assets/
│   │   │   │   │   └── .gitkeep
│   │   │   │   ├── environments/
│   │   │   │   │   └── environment.ts
│   │   │   │   ├── index.html
│   │   │   │   ├── main.ts
│   │   │   │   ├── polyfills.ts
│   │   │   │   └── styles.css
│   │   │   ├── tsconfig.app.json
│   │   │   └── tsconfig.json
│   │   ├── express/
│   │   │   ├── .dockerignore
│   │   │   ├── .gitignore
│   │   │   ├── Dockerfile
│   │   │   ├── README.md
│   │   │   ├── package.json
│   │   │   └── src/
│   │   │       └── server.js
│   │   ├── nextjs/
│   │   │   ├── .dockerignore
│   │   │   ├── .gitignore
│   │   │   ├── Dockerfile
│   │   │   ├── README.md
│   │   │   ├── components/
│   │   │   │   ├── HankoAuth.tsx
│   │   │   │   ├── HankoProfile.tsx
│   │   │   │   └── SessionExpiredModal.tsx
│   │   │   ├── next.config.js
│   │   │   ├── package.json
│   │   │   ├── pages/
│   │   │   │   ├── _app.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── profile.tsx
│   │   │   │   └── todo.tsx
│   │   │   ├── styles/
│   │   │   │   ├── Todo.module.css
│   │   │   │   └── index.css
│   │   │   ├── tsconfig.json
│   │   │   └── util/
│   │   │       └── TodoClient.ts
│   │   ├── react/
│   │   │   ├── .dockerignore
│   │   │   ├── .gitignore
│   │   │   ├── Dockerfile
│   │   │   ├── README.md
│   │   │   ├── index.html
│   │   │   ├── package.json
│   │   │   ├── public/
│   │   │   │   ├── manifest.json
│   │   │   │   └── robots.txt
│   │   │   ├── src/
│   │   │   │   ├── HankoAuth.tsx
│   │   │   │   ├── HankoProfile.tsx
│   │   │   │   ├── SessionExpiredModal.tsx
│   │   │   │   ├── Todo.module.css
│   │   │   │   ├── Todo.tsx
│   │   │   │   ├── TodoClient.ts
│   │   │   │   ├── index.css
│   │   │   │   ├── index.tsx
│   │   │   │   └── react-app-env.d.ts
│   │   │   ├── tsconfig.app.json
│   │   │   ├── tsconfig.json
│   │   │   ├── tsconfig.node.json
│   │   │   ├── vite-env.d.ts
│   │   │   └── vite.config.js
│   │   └── vue/
│   │       ├── .dockerignore
│   │       ├── .gitignore
│   │       ├── .prettierrc.json
│   │       ├── .vscode/
│   │       │   └── extensions.json
│   │       ├── Dockerfile
│   │       ├── README.md
│   │       ├── env.d.ts
│   │       ├── index.html
│   │       ├── package.json
│   │       ├── src/
│   │       │   ├── App.vue
│   │       │   ├── assets/
│   │       │   │   └── base.css
│   │       │   ├── components/
│   │       │   │   └── SessionExpiredModal.vue
│   │       │   ├── main.ts
│   │       │   ├── router/
│   │       │   │   └── index.ts
│   │       │   ├── utils/
│   │       │   │   └── TodoClient.ts
│   │       │   └── views/
│   │       │       ├── LoginView.vue
│   │       │       ├── ProfileView.vue
│   │       │       └── TodoView.vue
│   │       ├── tsconfig.app.json
│   │       ├── tsconfig.json
│   │       ├── tsconfig.node.json
│   │       └── vite.config.ts
│   ├── frontend-sdk/
│   │   ├── .dockerignore
│   │   ├── .eslintignore
│   │   ├── .eslintrc.cjs
│   │   ├── .gitignore
│   │   ├── .nvmrc
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── jest.config.cjs
│   │   ├── jsdoc.json
│   │   ├── nginx/
│   │   │   └── default.conf
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── Hanko.ts
│   │   │   ├── declarations.d.ts
│   │   │   ├── index.ts
│   │   │   └── lib/
│   │   │       ├── Cookie.ts
│   │   │       ├── Dto.ts
│   │   │       ├── Errors.ts
│   │   │       ├── Pkce.ts
│   │   │       ├── SessionStorage.ts
│   │   │       ├── Throttle.ts
│   │   │       ├── WebauthnSupport.ts
│   │   │       ├── client/
│   │   │       │   ├── Client.ts
│   │   │       │   ├── HttpClient.ts
│   │   │       │   ├── SessionClient.ts
│   │   │       │   └── UserClient.ts
│   │   │       ├── events/
│   │   │       │   ├── CustomEvents.ts
│   │   │       │   ├── Dispatcher.ts
│   │   │       │   ├── Listener.ts
│   │   │       │   ├── Relay.ts
│   │   │       │   ├── Scheduler.ts
│   │   │       │   ├── SessionChannel.ts
│   │   │       │   ├── SessionState.ts
│   │   │       │   └── WindowActivityManager.ts
│   │   │       └── flow-api/
│   │   │           ├── State.ts
│   │   │           ├── WebauthnManager.ts
│   │   │           ├── auto-steps.ts
│   │   │           ├── passkey-autofill-activation.ts
│   │   │           └── types/
│   │   │               ├── action.ts
│   │   │               ├── flow.ts
│   │   │               ├── flowError.ts
│   │   │               ├── input.ts
│   │   │               ├── payload.ts
│   │   │               └── state.ts
│   │   ├── tests/
│   │   │   ├── Hanko.spec.ts
│   │   │   ├── lib/
│   │   │   │   ├── Cookie.spec.ts
│   │   │   │   ├── Throttle.spec.ts
│   │   │   │   ├── WebauthnSupport.spec.ts
│   │   │   │   ├── client/
│   │   │   │   │   ├── HttpClient.spec.ts
│   │   │   │   │   └── UserClient.spec.ts
│   │   │   │   ├── events/
│   │   │   │   │   ├── Dispatcher.spec.ts
│   │   │   │   │   └── Listener.spec.ts
│   │   │   │   └── flow-api/
│   │   │   │       └── State.spec.ts
│   │   │   ├── setup.ts
│   │   │   └── types.d.ts
│   │   ├── tsconfig.json
│   │   └── tsconfig.prod.json
│   ├── package.json
│   └── turbo.json
├── quickstart/
│   ├── Dockerfile
│   ├── README.md
│   ├── go.mod
│   ├── go.sum
│   ├── main.go
│   ├── middleware/
│   │   ├── cache_control.go
│   │   └── session.go
│   └── public/
│       ├── assets/
│       │   └── css/
│       │       ├── common.css
│       │       ├── fonts.css
│       │       ├── index.css
│       │       └── secured.css
│       └── html/
│           ├── error.html
│           ├── index.html
│           ├── secured.html
│           └── unauthorized.html
└── skaffold.yaml

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
[**]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[**.{ts,tsx,json,js,cjs,sass}]
indent_style = space
indent_size = tab
tab_width = 2

[{go.mod,go.sum,*.go}]
indent_style = tab
indent_size = 4


================================================
FILE: .github/ISSUE_TEMPLATE/BUG_REPORT.yml
================================================
description: "Create a bug report"
labels:
  - bug
name: "Bug Report"
body:
  - attributes:
      value: "Thank you for taking the time to fill out this bug report!\n"
    type: markdown
  - attributes:
      label: Checklist
      options:
        - label:
            "I could not find a solution in the existing issues or docs."
          required: true
        - label:
            "I agree to follow this project's [Code of Conduct](https://github.com/teamhanko/hanko/blob/main/CODE_OF_CONDUCT.md)."
          required: true
    id: checklist
    type: checkboxes
  - attributes:
      description: "A clear and concise description of what the bug is."
      label: "Describe the bug"
      placeholder: "Tell us what you see!"
    id: describe-bug
    type: textarea
    validations:
      required: true
  - attributes:
      label: Reproducing the bug
      description: |
        Clear, formatted, and easy to follow steps to reproduce the behavior:
      placeholder: |
        Steps to reproduce the behavior:

        1. Run `docker run ....`
        2. Make API Request to with `curl ...`
        3. Request fails with response: `{"some": "error"}`
    id: reproduce-bug
    type: textarea
    validations:
      required: true
  - attributes:
      label: Logs
      description:
        "Please copy and paste any relevant log output. This will be
        automatically formatted into code, so no need for backticks. Please
        redact any sensitive information!"
      render: Shell
    id: logs
    type: textarea
  - attributes:
      label: Configuration
      description:
        "Please copy and paste any relevant configuration. This will be
        automatically formatted into code, so no need for backticks. Please
        redact any sensitive information!"
      render: yml
      placeholder: |
        server:
          public:
            address: :8080
    id: config
    type: textarea
  - attributes:
      label: Hanko Version
      description: "What version of our software are you running? Either version tag (e.g. v1.0.2) or commit short sha (e.g. 26b78e3)"
    id: version
    type: input
    validations:
      required: true
  - attributes:
      label: OS Hanko Backend
      description: "On which operating system is your hanko backend running?"
      options:
        - macOS
        - Linux
        - Windows
        - Other
    id: operating-system-hanko
    type: dropdown
  - attributes:
      label: OS Version Hanko Backend
      description: "Which operating system version are you running the hanko backend on?"
    id: operating-system-version-hanko
    type: textarea
  - attributes:
      label: OS
      description: "On which operating system are you observing this issue?"
      options:
        - macOS
        - Linux
        - Windows
        - Other
    id: operating-system-client
    type: dropdown
  - attributes:
      label: OS Version
      description: "Which operating system version are you using?"
    id: operating-system-version-client
    type: textarea
  - attributes:
      label: Browser Version
      description: "Which browser and which version are you using?"
    id: browser-version
    type: textarea
  - attributes:
      label: Environment
      description: "In which environment are you deploying/running the application(s)?"
      options:
        - Docker
        - Docker Compose
        - Binary/Build & Run from Source
        - Other (e.g. Kubernetes)
    id: deployment
    type: dropdown
  - attributes:
      description: "Add any other context (Links, References, Screenshots, Files) pertaining to the problem here."
      label: Additional Context
    id: additional
    type: textarea


================================================
FILE: .github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml
================================================
description:
  "Make a feature request"
labels:
  - enhancement
name: "Feature Request"
body:
  - attributes:
      value: |
        Thank you for making a feature request for this project!
    type: markdown
  - attributes:
      label: Checklist
      options:
        - label:
            "I could not find a solution in the existing issues or docs."
          required: true
        - label:
            "I agree to follow this project's [Code of Conduct](https://github.com/teamhanko/hanko/blob/main/CODE_OF_CONDUCT.md)."
          required: true
    id: checklist
    type: checkboxes
  - attributes:
      label: Description
      description:
        "Is your feature request related to a problem? Please describe."
      placeholder:
        "A clear and concise description of what the problem is. Ex. I'm always
        frustrated when [...]"
    id: problem
    type: textarea
    validations:
      required: true
  - attributes:
      description: |
        Describe the solution you'd like to see implemented
      placeholder: |
        A clear and concise description of what you want to happen.
      label: "Describe your ideal solution"
    id: solution
    type: textarea
    validations:
      required: false
  - attributes:
      label: Workarounds or alternatives
      description: "Describe alternatives you've considered"
    id: alternatives
    type: textarea
    validations:
      required: false
  - attributes:
      label: Hanko Version
      description: "What version of our software are you running?"
    id: version
    type: input
    validations:
      required: true
  - attributes:
      label: Additional Context
      description:
        "Add any other context or screenshots about the feature request here."
    id: additional
    type: textarea


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Hanko Discussions
    url: https://github.com/teamhanko/hanko/discussions/new?category=q-a
    about:
      If you have general questions or questions on integrating Hanko, please open up a new discussion in the Q&A section.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!-- Thank you for submitting a pull request for this project! This is a pull request template,
please remove any sections that are not applicable. -->

# Description

<!-- Brief description of WHAT you’re doing and WHY. -->

<!-- If applicable, add references to fixed/related issues (e.g. add "Fixes #<issue>"/"Relates to#<issue>".  -->

# Implementation

<!-- Brief description of HOW you achieved it. Perhaps give a high level description of the program flow. Did you need
to refactor something? What tradeoffs did you take? Are there any caveats/downsides to your solution? Are there things
in here which you’d particularly like people to pay close attention to? -->

# Tests

<!-- Describe how to verify your changes. Provide instructions for the purpose of reproducibility. List any relevant
details for your test configuration. -->

# Todos

<!-- Are there any other outstanding issues or tasks that must be solved before this change can be possibly merged? -->

# Additional context

<!-- Add any other relevant context pertaining to the proposed change. For example, if the change can be visualized,
include screenshots or diagrams to indicate the state before and after the change. -->


================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
  - package-ecosystem: "gomod"
    directory: "/backend"
    schedule:
      interval: "daily"

  - package-ecosystem: "npm"
    directory: "/frontend"
    schedule:
      interval: "daily"
    # Always increase the version requirement
    # to match the new version.
    versioning-strategy: increase

  - package-ecosystem: "github-actions"
    # Workflow files stored in the default location of `.github/workflows`.
    # (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.)
    directory: "/"
    schedule:
      interval: "weekly"


================================================
FILE: .github/workflows/build-frontend.yml
================================================
name: build-frontend

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Setup Node
        uses: actions/setup-node@v6
        with:
          node-version: v24.11.1

      - name: Install dependencies
        working-directory: ./frontend
        run: npm ci

      - name: Build
        working-directory: ./frontend
        run: npm run build

      - name: Run tests
        working-directory: ./frontend
        run: npm test


================================================
FILE: .github/workflows/cli-publish.yml
================================================
name: CLI

on:
  release:
    types: [published]

permissions:
  contents: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - uses: actions/setup-go@v6
        with:
          go-version: stable
      - uses: goreleaser/goreleaser-action@v7
        with:
          distribution: goreleaser
          version: latest
          args: release --clean --snapshot
          workdir: ./backend
        env:
          GORELEASER_CURRENT_TAG: 'v0.0.0' # can be anything since we're using --snapshot
      - run: |
          mkdir -p dist/artifacts
          cp dist/*.{tar.gz,zip} dist/artifacts
        working-directory: ./backend
      - name: Upload assets
        uses: softprops/action-gh-release@v2
        with:
          files: ./backend/dist/artifacts/*
          fail_on_unmatched_files: true


================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ main ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ main ]
  schedule:
    - cron: '22 12 * * 3'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'go', 'javascript' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

    steps:
    - name: Checkout repository
      uses: actions/checkout@v6

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v4
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.
        
        # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
        # queries: security-extended,security-and-quality

        
    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v4

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

    #   If the Autobuild fails above, remove it and uncomment the following three lines. 
    #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

    # - run: |
    #   echo "Run, Build Application using script"
    #   ./location_of_script_within_repo/buildscript.sh

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v4


================================================
FILE: .github/workflows/docker-publish.yml
================================================
name: Docker

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

on:
  push:
    branches: [ main ]
    # Publish semver tags as releases.
    tags: [ 'backend/v*.*.*' ]
  pull_request:
    branches: [ main ]

env:
  # Use docker.io for Docker Hub if empty
  REGISTRY: ghcr.io
  # github.repository as <account>/<repo>
  IMAGE_NAME: ${{ github.repository }}


jobs:
  build-and-push:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - context: ./backend
            image: ghcr.io/teamhanko/hanko
          - context: ./quickstart
            image: ghcr.io/teamhanko/hanko/quickstart
          - context: ./frontend
            image: ghcr.io/teamhanko/hanko/elements
    permissions:
      contents: read
      packages: write
      # This is used to complete the identity challenge
      # with sigstore/fulcio when running outside of PRs.
      id-token: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v4

      - name: Setup Docker buildx
        uses: docker/setup-buildx-action@v4

      # Login against a Docker registry except on PR
      # https://github.com/docker/login-action
      - name: Log into registry ${{ env.REGISTRY }}
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v4
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # Extract metadata (tags, labels) for Docker
      # https://github.com/docker/metadata-action
      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ matrix.image }}
          tags: |
            type=schedule
            type=ref,event=branch,suffix=-{{sha}}-{{date 'YYYY-MM-DD.HHmmss'}}
            type=match,pattern=backend/(v\d+.\d+.\d+),group=1,event=tag
            type=ref,event=pr

      # Build and push Docker image with Buildx (don't push on PR)
      # https://github.com/docker/build-push-action
      - name: Build and push Docker image
        id: build-and-push
        uses: docker/build-push-action@v7
        with:
          platforms: linux/amd64,linux/arm64
          context: ${{ matrix.context }}
          file: ${{ matrix.context }}/Dockerfile
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/e2e.yml
================================================
name: E2E Test

on:
  workflow_dispatch:

jobs:
  passwordless:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Start containers
        working-directory: ./deploy/docker-compose
        run: docker compose -f "quickstart.e2e.yaml" up -d --build

      - name: Install dependencies
        working-directory: ./e2e
        run: |
          npm install
          npx playwright install chromium

      - name: Run tests
        working-directory: ./e2e
        run: npm run 'test:nopw'

      - name: Stop containers
        if: always()
        working-directory: ./deploy/docker-compose
        run: docker compose -f "quickstart.e2e.yaml" down
  passwords:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Start containers
        working-directory: ./deploy/docker-compose
        run: docker compose -f "quickstart.e2e.yaml" up -d --build
        env:
          PASSWORD_ENABLED: true

      - name: Install dependencies
        working-directory: ./e2e
        run: |
          npm install
          npx playwright install chromium

      - name: Run tests
        working-directory: ./e2e
        run: npm run 'test:pw'

      - name: Stop containers
        if: always()
        working-directory: ./deploy/docker-compose
        run: docker compose -f "quickstart.e2e.yaml" down
  nosignup:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Copy config
        working-directory: ./deploy/docker-compose
        run: cp config-disable-signup.yaml config.yaml

      - name: Start containers
        working-directory: ./deploy/docker-compose
        run: docker compose -f "quickstart.e2e.yaml" up -d --build

      - name: Install dependencies
        working-directory: ./e2e
        run: |
          npm install
          npx playwright install chromium

      - name: Run tests
        working-directory: ./e2e
        run: npm run 'test:nosignup'

      - name: Stop containers
        if: always()
        working-directory: ./deploy/docker-compose
        run: docker compose -f "quickstart.e2e.yaml" down


================================================
FILE: .github/workflows/go.yml
================================================
name: Go

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6

    - name: Set up Go
      uses: actions/setup-go@v6
      with:
        go-version: '1.26'

    - name: Build
      working-directory: ./backend
      run: |
        go generate ./...
        go build -v ./...

    - name: Test
      working-directory: ./backend
      run: go test -v ./...


================================================
FILE: .github/workflows/release-frontend-sdk.yml
================================================
name: Release @teamhanko/frontend-sdk

on:
  push:
    tags:
      - '@teamhanko/frontend-sdk@*'

defaults:
  run:
    working-directory: frontend/frontend-sdk

permissions:
  contents: write  # Added to allow pushing to gh-pages
  id-token: write  # Required for OIDC

jobs:
  check-matching-versions:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: get-npm-version
        id: package-version
        uses: martinbeentjes/npm-get-version-action@main
        with:
          path: frontend/frontend-sdk
      - run: |
          version=$(echo $GITHUB_REF | cut -f3 -d'@')
          echo "git_tag_version=$version" >> $GITHUB_OUTPUT
        id: tag-version
      - run: echo ${{ steps.tag-version.outputs.git_tag_version }}
      - name: Version correctly set check
        if: steps.package-version.outputs.current-version != steps.tag-version.outputs.git_tag_version
        uses: actions/github-script@v8
        with:
          script: |
            core.setFailed('version in package.json is not equal to git tag version!')

  build-and-publish:
    needs: check-matching-versions
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 24.11.1
          registry-url: https://registry.npmjs.org/
      - run: npm ci
      - run: npm run build
      - run: npm test
      - name: publish frontend-sdk
        run: npm publish

  generate-docs:
    needs: build-and-publish
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
      - name: Setup node
        uses: actions/setup-node@v6
        with:
          node-version: 24.11.1
      - name: Install dependencies
        run: npm ci
      - name: Generate docs
        run: npm run docs
      - name: Deploy to gh-pages branch
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: frontend/frontend-sdk/.generated/docs
          destination_dir: jsdoc/hanko-frontend-sdk


================================================
FILE: .github/workflows/release-hanko-elements.yml
================================================
name: Release @teamhanko/hanko-elements

on:
  push:
    tags:
      - '@teamhanko/hanko-elements@*'

defaults:
  run:
    working-directory: frontend/elements

permissions:
  id-token: write  # Required for OIDC

jobs:
  check-matching-versions:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: get-npm-version
        id: package-version
        uses: martinbeentjes/npm-get-version-action@main
        with:
          path: frontend/elements
      - run: |
          version=$(echo $GITHUB_REF | cut -f3 -d'@')
          echo "git_tag_version=$version" >> $GITHUB_OUTPUT
        id: tag-version
      - run: echo ${{ steps.tag-version.outputs.git_tag_version }}
      - name: Version correctly set check
        if: steps.package-version.outputs.current-version != steps.tag-version.outputs.git_tag_version
        uses: actions/github-script@v8
        with:
          script: |
            core.setFailed('version in package.json is not equal to git tag version!')

  build-and-publish:
    needs: check-matching-versions
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 24.11.1
          registry-url: https://registry.npmjs.org/
      - name: build-frontend-sdk
        run: |
          npm ci
          npm run build
        working-directory: frontend/frontend-sdk
      - name: build-elements
        run: |
          npm ci
          npm run build
      - name: publish-elements
        run: npm publish


================================================
FILE: .github/workflows/schema-generate-config.yml
================================================
name: Generate config JSON schema

on:
  push:
    branches:
      - main
    paths:
      - 'backend/config/*.go'
      - '!backend/config/config_default.go'
      - '!backend/config/**test.go'

jobs:
  config:
    runs-on: ubuntu-latest
    steps:
      - name: Set up Go
        uses: actions/setup-go@v6
        with:
          go-version: '1.24'

      - name: Checkout backend
        uses: actions/checkout@v6
        with:
          path: hanko

      - name: Generate config JSON schema
        working-directory: ./hanko/backend
        run: |
          go generate ./...
          go run main.go schema generate config

      - name: Create pull request
        working-directory: ./hanko
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add .
          if ! git diff-index --quiet HEAD; then
            git checkout -b "chore-autogenerate-config-json-schema-${{ github.run_id }}"
            git commit -m "chore: autogenerate config JSON schema"
            git push origin HEAD
            gh pr create \
              -B main \
              -H "chore-autogenerate-config-json-schema-${{ github.run_id }}" \
              -t "chore: autogenerate config JSON schema" \
              -b '# Description

             Autogenerate config JSON schema. Created by Github Action.

             # Additional context

             Ref: `${{ github.ref }}`
             Workflow name: `${{ github.workflow }}`
             Job ID: `${{ github.job }}`
             Run ID: `${{ github.run_id }}`'
          else
            echo "No changes detected, skipping pull request creation."
          fi
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}



================================================
FILE: .github/workflows/schema-generate-import.yml
================================================
name: Generate import JSON schema

on:
  push:
    branches:
      - main
    paths:
      - backend/cmd/user/format.go

jobs:
  import:
    runs-on: ubuntu-latest
    steps:
      - name: Set up Go
        uses: actions/setup-go@v6
        with:
          go-version: '1.24'

      - name: Checkout backend
        uses: actions/checkout@v6
        with:
          path: hanko

      - name: Generate import JSON schema
        working-directory: ./hanko/backend
        run: |
          go generate ./...
          go run main.go schema generate import

      - name: Create pull request
        working-directory: ./hanko
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add .
          if ! git diff-index --quiet HEAD; then
            git checkout -b "chore-autogenerate-import-json-schema-${{ github.run_id }}"
            git commit -m "chore: autogenerate import JSON schema"
            git push origin HEAD
            gh pr create \
              -B main \
              -H "chore-autogenerate-import-json-schema-${{ github.run_id }}" \
              -t "chore: autogenerate import JSON schema" \
              -b '# Description

             Autogenerate import JSON schema. Created by Github Action.

             # Additional context

             Ref: `${{ github.ref }}`
             Workflow name: `${{ github.workflow }}`
             Job ID: `${{ github.job }}`
             Run ID: `${{ github.run_id }}`'
          else
            echo "No changes detected, skipping pull request creation."
          fi
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/schema-markdown-config.yml
================================================
name: Generate config reference markdown

on:
  push:
    tags:
      - 'backend/*'
  workflow_dispatch:

jobs:
  config:
    runs-on: ubuntu-latest
    steps:
      - name: Set up Go
        uses: actions/setup-go@v6
        with:
          go-version: '1.24'

      - uses: actions/setup-node@v6
        with:
          node-version: '20.16.0'
          registry-url: https://registry.npmjs.org/

      - name: Checkout backend
        uses: actions/checkout@v6
        with:
          path: hanko

      - name: Checkout backend wiki
        uses: actions/checkout@v6
        with:
          repository: ${{github.repository}}.wiki
          path: wiki

      - name: Generate config markdown
        working-directory: ./hanko/backend
        run: |
          go generate ./...
          go run main.go schema markdown config

      - name: Strip links of .md file endings
        working-directory: ./hanko/backend
        run: |
          find ./.generated/docs/config -type f -name "*.md" -exec sed -i "s/\.md//g" "{}" \;

      - name: Prepend version information
        working-directory: ./hanko/backend
        run: |
          version=$(echo $GITHUB_REF_NAME | cut -f2 -d'/')
          find ./.generated/docs/config -type f -name '*.md' -exec sed -i "1i\\
          ## Version\\
          \\
          \`$version\`\\
          \\
          " {} \;

      - name: Copy generated files
        working-directory: ./hanko/backend
        run: |
          mkdir -p $GITHUB_WORKSPACE/wiki/reference/config
          rm $GITHUB_WORKSPACE/wiki/reference/config/*.md 2>/dev/null || true
          cp .generated/docs/config/*.md $GITHUB_WORKSPACE/wiki/reference/config

      - name: Commit and push to wiki
        working-directory: ./wiki
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add .
          if ! git diff-index --quiet HEAD; then
            version=$(echo $GITHUB_REF_NAME | cut -f2 -d'/')
            git commit -m "chore: autogenerate config reference for $version"
            git pull origin master --rebase
            git push origin HEAD
          else
            echo "No changes detected, skipping commit and push."
          fi


================================================
FILE: .github/workflows/schema-markdown-import.yml
================================================
name: Generate user import reference markdown

on:
  push:
    tags:
      - 'backend/*'
  workflow_dispatch:

jobs:
  import:
    runs-on: ubuntu-latest
    steps:
      - name: Set up Go
        uses: actions/setup-go@v6
        with:
          go-version: '1.24'

      - uses: actions/setup-node@v6
        with:
          node-version: '20.16.0'
          registry-url: https://registry.npmjs.org/

      - name: Checkout backend
        uses: actions/checkout@v6
        with:
          path: hanko

      - name: Checkout backend wiki
        uses: actions/checkout@v6
        with:
          repository: ${{github.repository}}.wiki
          path: wiki

      - name: Generate import markdown
        working-directory: ./hanko/backend
        run: |
          go generate ./...
          go run main.go schema markdown import

      - name: Strip links of .md file endings
        working-directory: ./hanko/backend
        run: |
          find ./.generated/docs/import -type f -name "*.md" -exec sed -i "s/\.md//g" "{}" \;

      - name: Prepend version information
        working-directory: ./hanko/backend
        run: |
          version=$(echo $GITHUB_REF_NAME | cut -f2 -d'/')
          find ./.generated/docs/import -type f -name '*.md' -exec sed -i "1i\\
          ## Version\\
          \\
          \`$version\`\\
          \\
          " {} \;

      - name: Copy generated files
        working-directory: ./hanko/backend
        run: |
          mkdir -p $GITHUB_WORKSPACE/wiki/reference/import
          rm $GITHUB_WORKSPACE/wiki/reference/import/*.md 2>/dev/null || true
          cp .generated/docs/import/*.md $GITHUB_WORKSPACE/wiki/reference/import

      - name: Delay check
        run: |
          OWNER=${{ github.repository_owner }}
          NAME="$(cut -f2 -d'/' <<< ${{ github.repository }})"
          HEAD_SHA="${{ github.sha }}"
          STATUS="in_progress"
          BASE="/repos/${OWNER}/${NAME}/actions/workflows/schema-markdown-config.yml/runs"
          REQUEST_URL="${BASE}?head_sha=${HEAD_SHA}&status=${STATUS}"
          SHOULD_DELAY=$(gh api "$REQUEST_URL" --jq '.total_count > 0')
          if [ "$SHOULD_DELAY" = "true" ]; then
            echo "Config markdown generation job in progress, delaying to avoid conflicts"
            sleep 60s
          else
            echo "No config markdown generation job in progress, continuing"
          fi
        shell: bash
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Commit and push to wiki
        working-directory: ./wiki
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add .
          if ! git diff-index --quiet HEAD; then
            version=$(echo $GITHUB_REF_NAME | cut -f2 -d'/')
            git commit -m "chore: autogenerate import reference for $version"
            git pull origin master --rebase
            git push origin HEAD
          else
            echo "No changes detected, skipping commit and push."
          fi


================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

#env files
*.env

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Generated files
.generated

# MacOS
.DS_Store

## JetBrains
.idea

hanko

.turbo

# Dependency directories (remove the comment below to include it)
node_modules
dist
e2e/test-results/
e2e/playwright-report/
e2e/playwright/.cache/
/backend/build_info/version.txt
/backend/dist


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
  address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
coc@hanko.io.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,  harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Hanko

Thank you for considering contributing to Hanko! Following are the guidelines we would like you to follow:

- [Code of Conduct](#code-of-conduct)
- [Communication](#communication)
- [Reporting Issues](#reporting-issues)
  - [Security](#security)
  - [Bugs](#bugs)
- [Feature Requests](#feature-requests)
- [Submitting Code](#submitting-code)
- [Commit Message Guidelines](#commit-message-guidelines)
- [Style Guidelines](#style-guidelines)

## Code of Conduct

We expect all contributors to adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md).

## Communication

If you have any questions, want to discuss bugs or feature requests, or just want talk to other Hanko users you are welcome
to join our [Slack](https://hanko.io/community) community or use the [Hanko Discussions](https://github.com/teamhanko/hanko/discussions)
(especially useful for long term discussion or larger questions).

## Reporting issues

Reporting issues requires a [GitHub](https://github.com/) account. Please do not use the issue
tracker for general support questions but use the above mentioned [communication](#communication) channels.

### Security

Pursuant to our [security policy](./SECURITY.md), any security vulnerabilities should be reported directly to
`security@hanko.io` instead of using the issue tracker.

### Bugs

Bugs are tracked as [GitHub issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues).
When reporting a bug choose "Bug Report" when [creating](https://github.com/teamhanko/hanko/issues/new/choose) a new
issue. Doing so will present you with a template form to fill out. Be as detailed as possible. Good
bug reports are vital, so thank you for taking the time!

Before reporting a bug:

1. Take a look at the [existing issues](https://github.com/teamhanko/hanko/issues?q=is%3Aissue+label%3Abug) and make
   sure the issue hasn't already been reported. If you find a similar bug and the issue is still open, consider adding
   a comment providing any new information that you might be able to report.
2. Make sure the issue hasn't been fixed already. Try to reproduce it using the latest `main` branch in the repository if
   you were not working with the latest version.
3. Make sure the bug is really a bug. If you need general support or are unsure whether some behaviour represents a bug
   please do not file a bug ticket but reach out through the above mentioned [communication](#communication) channels.
4. Gather as much information about the bug as you can. Logs, screenshots/screen captures, steps to reproduce the bug
   can be vital for a useful bug report.

If you already have suggestions on how to fix the bug, do not hesitate to include them in the bug description.

## Feature requests

Just like bugs, feature requests are tracked as [GitHub issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues).
When suggesting an enhancement choose "Feature Request" when [creating](https://github.com/teamhanko/hanko/issues/new/choose) a new
issue and fill out the template form.

Before making a feature request:

1. Take a look at the [existing issues](https://github.com/teamhanko/hanko/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) and make
   sure the issue hasn't already been reported.
2. Make sure the issue hasn't been implemented already. Always try using the latest `main` branch in the repository if
   to confirm the feature is not already in place.

When filling out the template form, be sure to be as detailed as possible. Describe the current behavior and explain
which behavior you expect to see instead. Explain why this enhancement would be useful to Hanko users.

## Submitting Code

Contributing code requires a [GitHub](https://github.com/) account. All contributions are made via
[pull requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests).
Pull requests should target the `main` branch.

To submit your code:

1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo#forking-a-repository) the [repository](https://github.com/teamhanko/hanko).
2. [Clone](https://docs.github.com/en/get-started/quickstart/fork-a-repo#cloning-your-forked-repository) the forked repository.
3. [Configure remotes](https://docs.github.com/en/get-started/quickstart/fork-a-repo#configuring-git-to-sync-your-fork-with-the-original-repository).
4. Create a new [branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches)
   off of the `main` branch.
   ```
   git checkout -b <new-branch-name>
   ```
5. Make your changes. Make sure to follow the [Style Guidelines](#style-guidelines). Commit your changes.
   ```
   git add -A
   git commit
   ```
   Commit messages should follow the [Commit Message Guidelines](#commit-message-guidelines).
6. Make sure to update, or add to any tests where appropriate. Try to run tests locally first (`go test ./...` for the
   `backend`, see the [README](./e2e/README.md) for the `e2e`tests on how to run them).
7. Push your feature branch up to your fork:
   ```
   git push origin <feature-branch-name>
   ```
8. [Create a pull request from your fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork).
9. Submit the pull request by filling out the [pull request template](./.github/PULL_REQUEST_TEMPLATE.md)
    (note: the template should be displayed automatically once you open a pull request; take account of the comments in
    the displayed template).
10. If a pull request is not ready to be reviewed it should be marked as a "Draft".


When pull requests fail test checks, authors are expected to update
their pull requests to address the failures until the tests pass. If you have trouble or questions on how to add to
existing tests, reach out through our [communication](#communication) channels.

# Commit Message Guidelines

Commit messages should adhere to the
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
The commit message should be structured as follows:

```
<type>(<optional scope>): <description>

<optional body>

<optional footer(s)>
```

The commit message headline should have the following structure:
```
<type>(<optional scope>): <description>
   │            │               │
   │            │               └─⫸ Summary in present tense. Not capitalized. No period at the end.
   │            │
   │            └─⫸ Commit Scope: optional
   │
   └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test|chore
```
The `<type>` should be one of the following:
* **build**: Changes that affect the build system
* **ci**: Changes that affect the CI workflows (e.g. changes to `.github` CI configuration files)
* **docs**: Documentation only changes (this includes both content in the `docs` as well as changes to readmes)
* **feat**: A new feature
* **fix**: A bug fix
* **perf**: A code change that improves performance
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **test**: Adding missing tests or correcting existing tests
* **chore**: Anything that cannot be categorized properly using the above prefixes (e.g. increasing versions)

The `<scope>` is optional. If present, it should be the name of the (npm) package or directory affected by the changes of
the commit.

# Style Guidelines

## Go

Go files should be [formatted](https://go.dev/blog/gofmt) according to gofmt's rules.

```
# single file
go fmt path/to/changed/file.go

# all files, e.g. in 'backend' directory
go fmt ./...
```


================================================
FILE: LICENSE
================================================
  Portions of this software are licensed as follows:

  All content that resides under the "elements" directory
(https://github.com/teamhanko/hanko/tree/main/frontend/elements) of this repository is
licensed under the license defined in "elements/LICENSE".

  All content that resides under the "frontend-sdk" directory
(https://github.com/teamhanko/hanko/tree/main/frontend/frontend-sdk) of this repository is
licensed under the license defined in "frontend-sdk/LICENSE".

  All third party components incorporated into Hanko software are licensed
under the original license provided by the owner of the applicable component.

  Content outside of the above mentioned directories or restrictions above is
available under the "AGPLv3" license as defined below.

--

                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU Affero General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time.  Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.


================================================
FILE: README.md
================================================
<p align="center">
  <img width="300" src="https://user-images.githubusercontent.com/20115649/176922807-fb92327a-15d5-4568-a4e7-78093cea045e.svg?sanitize=true#gh-light-mode-only">
  <img width="300" src="https://user-images.githubusercontent.com/20115649/176922819-61dfb644-529f-4f81-a577-7daa47185300.svg?sanitize=true#gh-dark-mode-only">
</p>

---
[![Test Status](https://github.com/teamhanko/hanko/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/teamhanko/hanko/actions/workflows/codeql-analysis.yml)
[![Build Status](https://github.com/teamhanko/hanko/workflows/Go/badge.svg)](https://github.com/teamhanko/hanko/actions/workflows/go.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/teamhanko/hanko)](https://goreportcard.com/report/github.com/teamhanko/hanko)
[![GoDoc](https://godoc.org/github.com/teamhanko/hanko?status.svg)](https://godoc.org/github.com/teamhanko/hanko)
[![npm (scoped)](https://img.shields.io/npm/v/@teamhanko/hanko-elements?label=hanko-elements)](https://www.npmjs.com/package/@teamhanko/hanko-elements)
[![npm (scoped)](https://img.shields.io/npm/v/@teamhanko/hanko-frontend-sdk?label=hanko-frontend-sdk)](https://www.npmjs.com/package/@teamhanko/hanko-frontend-sdk)

# About Hanko
Hanko is an open source authentication and user management solution that is easy to integrate, framework-agnostic, and built on privacy-first principles like data minimalism and phishing resistance.

- Supports all modern authentication methods: passwords, MFA, passkeys, social logins, and SAML SSO
- Flexible configuration options, including passkey-only, OAuth-only, and user-deletable passwords
- Easy integration with **Hanko Elements** web components
- A robust API that handles all authentication and onboarding flow states, enabling fast, reliable custom frontend implementations
- API-first, lightweight, cloud-native

Available for self-hosting and as a fully managed service on [Hanko Cloud](https://www.hanko.io).

# Features
To follow the development of this project, watch our releases, leave a star, sign up to our [Product News](https://www.hanko.io/updates) or join our [Discord Community](https://www.hanko.io/community). Here's a brief overview of Hanko's current and upcoming features:

| Status | Feature |
|:------:| :--- |
|✅| Email / username identifiers |
|✅| Passwords, passcodes, passkeys |
|✅| Hanko Elements web components |
|✅| OAuth SSO (Sign in with Apple/Google/GitHub and more) |
|✅| i18n & custom translations |
|✅| SAML Enterprise SSO |
|✅| Webhooks |
|✅| Server-side sessions & remote session revocation |
|✅| MFA (TOTP, security keys) |
|✅| Custom OIDC/OAuth connections |
|✅| JS SDK |
|⚙️| Organizations, Roles, Permissions |
| | `<hanko-menu>` web component |
| | iOS, Android, React Native, Flutter SDKs |

Visit our [Roadmap](https://www.hanko.io/roadmap) for more information on upcoming features.

# Contact us
Schedule a demo with the team. Learn how you can built state-of-the-art authentication for your apps effortlessly with Hanko.

<a target="_blank" href="https://cal.com/team/hanko/demo"><img alt="Book us with Cal.com"  src="https://cal.com/book-with-cal-light.svg" /></a>

# Architecture
The main building blocks of the Hanko project are
- [backend](/backend/README.md) - Scalable, robust, and lightweight authentication API for passwords, passkeys, email passcodes, OAuth SSO, user and session management, and JWT issuing
- [hanko-elements](/frontend/elements/README.md) - Web components made for the Hanko API that provide onboarding, login, and user profile functionality and are customizable with CSS
- [hanko-frontend-sdk](/frontend/frontend-sdk/README.md) - A client package for using the Hanko API

The remainder of the repository consists of:
- [quickstart](/quickstart) - A quickstart example app showing off Hanko's login experience and acting as a reference implementation
- [examples](frontend/examples) - Example implementations for a number of frameworks
- docs - The Hanko documentation ([docs.hanko.io](https://docs.hanko.io)) -> Moved to its own repo here: https://github.com/teamhanko/docs

# Getting started
1. Try our hosted [live example](https://example.hanko.io) and our companion page [passkeys.io](https://www.passkeys.io) or use the [quickstart app](/quickstart/README.md) to get a feel for the user experience provided by an application that leverages the Hanko backend API and our custom web component
2. To run the project locally, there are two options available:
   - Bare metal:
      - Head over to the [backend](/backend/README.md) section to learn how to get it up and running for your own project. Use [Hanko Cloud](https://cloud.hanko.io) for a hosted backend.
   - Docker:
     -  If you prefer to use [Docker](https://www.docker.com/) to run the project locally, please visit the [Run the quickstart](./quickstart/README.md#run-the-quickstart) for information on how to run the project. This will create everything, including frontend and backend components. 
        -  If you wish to keep only the backend components, you can modify the [quickstart.yaml](./deploy/docker-compose/quickstart.yaml) to remove the unnecessary services. To make changes to the configuration to meet your needs, modify [config.yaml](./deploy/docker-compose/config.yaml).
3. Then, integrate [hanko-elements](/frontend/elements/README.md) – we provide [example applications](frontend/examples/README.md) and [guides](https://docs.hanko.io/guides/frontend) for your favourite frontend framework in the official documentation

If you want to use the Hanko backend API but prefer to build your own UI, you can still make use of the [hanko-frontend-sdk](/frontend/frontend-sdk/README.md). It forms the basis of our web components, and the client it provides handles communication with the [Hanko backend API](https://docs.hanko.io/api-reference/introduction) and saves you the time of rolling your own.

# Community
## Questions, bugs, ideas
If you have any questions or issues, please check this project's [Q&A section in discussions](https://github.com/teamhanko/hanko/discussions/categories/q-a) and the [open issues](https://github.com/teamhanko/hanko/issues). Feel free to comment on existing issues or create a new issue if you encounter any bugs or have a feature request. For yet unanswered questions, feedback, or new ideas, please open a new discussion.

## Discord community & X
We invite you to join our growing [Discord Community](https://www.hanko.io/community) if you want to get the latest updates on passkeys, WebAuthn, and this project or if you just want to chat with us. You can also [follow us on X](https://x.com/hanko_io).

# Licenses
[hanko-elements](frontend/elements) and [hanko-frontend-sdk](frontend/frontend-sdk) are licensed under the [MIT License](frontend/elements/LICENSE). Everything else in this repository, including [hanko backend](backend), is licensed under the [AGPL-3.0](/LICENSE). Non-Copyleft commercial licensing is available on request.


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Reporting a Vulnerability

If you discover a security vulnerability, we appreciate any information about it so that we can fix the problem as soon as possible.

Please email us at security@hanko.io.

- We will evaluate the report and respond within 3 business days
- We will keep your report confidential and will not share your personal information with any third party without your consent
- We will keep you informed of the progress until the problem is resolved


================================================
FILE: backend/.goreleaser.yaml
================================================
before:
  hooks:
    - go mod tidy
    - go generate ./...
builds:
  - env:
      - CGO_ENABLED=0
    goos:
      - linux
      - windows
      - darwin

archives:
  - format: tar.gz
    name_template: >-
      {{ .ProjectName }}_
      {{- title .Os }}_
      {{- if eq .Arch "amd64" }}x86_64
      {{- else if eq .Arch "386" }}i386
      {{- else }}{{ .Arch }}{{ end }}
      {{- if .Arm }}v{{ .Arm }}{{ end }}
    format_overrides:
    - goos: windows
      format: zip


================================================
FILE: backend/Dockerfile
================================================
# Build the hanko binary
FROM --platform=$BUILDPLATFORM golang:1.26 AS builder

ARG TARGETARCH

WORKDIR /workspace
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download

# Copy the go source
COPY main.go main.go
COPY cmd cmd/
COPY config config/
COPY persistence persistence/
COPY server server/
COPY handler handler/
COPY crypto crypto/
COPY dto dto/
COPY ee/saml ee/saml/
COPY session session/
COPY mail mail/
COPY audit_log audit_log/
COPY pagination pagination/
COPY rate_limiter rate_limiter/
COPY thirdparty thirdparty/
COPY build_info build_info/
COPY middleware middleware/
COPY template template/
COPY utils utils/
COPY mapper mapper/
COPY webhooks webhooks/
COPY flow_api flow_api/
COPY flowpilot flowpilot/

# Build
RUN go generate ./...
RUN CGO_ENABLED=0 GOOS=linux GOARCH="$TARGETARCH" go build -a -o hanko main.go

# Use distroless as minimal base image to package hanko binary
# See https://github.com/GoogleContainerTools/distroless for details
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /workspace/hanko .
USER 65532:65532

ENTRYPOINT ["/hanko"]


================================================
FILE: backend/Dockerfile.debug
================================================
# Build the hanko binary
FROM golang:1.26 AS builder
WORKDIR /workspace

# Get Delve
RUN CGO_ENABLED=0 GOOS=linux GOARCH="$TARGETARCH" go install github.com/go-delve/delve/cmd/dlv@latest

COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download

# Copy the go source
COPY ../main.go main.go
COPY cmd cmd/
COPY config config/
COPY persistence persistence/
COPY server server/
COPY handler handler/
COPY crypto crypto/
COPY dto dto/
COPY ee/saml ee/saml/
COPY session session/
COPY mail mail/
COPY audit_log audit_log/
COPY pagination pagination/
COPY rate_limiter rate_limiter/
COPY thirdparty thirdparty/
COPY build_info build_info/
COPY middleware middleware/
COPY flow_api flow_api/
COPY flowpilot flowpilot/
COPY template template/
COPY utils utils/
COPY mapper mapper/
COPY webhooks webhooks/

# Build
RUN go generate ./...
RUN CGO_ENABLED=0 GOOS=linux GOARCH="$TARGETARCH" go build -gcflags="all=-N -l" -a -o hanko main.go

# Use distroless as minimal base image to package hanko binary
# See https://github.com/GoogleContainerTools/distroless for details
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /go/bin/dlv .
COPY --from=builder /workspace/hanko .
USER 65532:65532

EXPOSE 8000 8001 40000

ENTRYPOINT ["/dlv", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/hanko", "--"]


================================================
FILE: backend/README.md
================================================
# Hanko backend

Hanko backend provides an HTTP API to build a modern login and registration experience for your users. Its core features
are an API for passkeys (WebAuthn), passwords, and passcodes, as well as JWT management.

Hanko backend can be used on its own or in combination with [hanko-elements](../frontend/elements), a powerful frontend library
that contains polished and customizable UI flows for password-based and passwordless user authentication that can be
easily integrated into any web app with as little as two lines of code.

# Contents

- [API features](#api-features)
- [Running the backend](#running-the-backend)
- [Running tests](#running-tests)
- [Additional topics](#additional-topics)
  - [Enabling password authentication](#enabling-password-authentication)
  - [Cross-domain communication](#cross-domain-communication)
  - [Audit logs](#audit-logs)
  - [Rate Limiting](#rate-limiting)
  - [Social connections](#social-connections)
    - [Built-in providers](#built-in-providers)
    - [Custom OAuth/OIDC providers](#custom-oauthoidc-providers)
    - [Account linking](#account-linking)
  - [User metadata](#user-metadata)
  - [User import](#user-import)
  - [Webhooks](#webhooks)
  - [Session JWT templates](#session-jwt-templates)
- [API specification](#api-specification)
- [Configuration reference](#configuration-reference)
- [License](#license)

## API features

- Passkeys (WebAuthn)
- Passcodes
- Passwords
- Email verification
- 2FA (TOTP, security keys)
- JWT management
- Sessions
- User management
- OAuth/OIDC SSO identity providers
- SAML
- Webhooks

## Running the backend

> **Note** If you just want to jump right into the experience of passkeys and passcodes, head over to the
> [quickstart guide](../quickstart/README.md).

To get the Hanko backend up and running you need to:

1. [Run a database](#run-a-database)
2. [Configure database access](#configure-database-access)
3. [Apply database migrations](#apply-database-migrations)
4. [Run and configure an SMTP server](#run-and-configure-an-smtp-server)
5. [Configure JSON Web Key Set generation](#configure-json-web-key-set-generation)
6. [Configure WebAuthn](#configure-webauthn)
7. [Configure CORS](#configure-cors)
8. [Start the backend](#start-the-backend)

### Run a database

The following databases are currently supported:

- PostgreSQL
- MySQL

#### Postgres

Use Docker to run a container based on the official [Postgres](https://hub.docker.com/_/postgres) image:

```shell
docker run --name=postgres \
-e POSTGRES_USER=<DB_USER> \
-e POSTGRES_PASSWORD=<DB_PASSWORD> \
-e POSTGRES_DB=<DB_DATABASE> \
-p <DB_PORT>:5432 \
-d postgres
```

or use the [official binary packages](https://www.postgresql.org/download/) to install and run
a Postgres instance.

#### MySQL

Use Docker to run a container based on the official [MySQL](https://hub.docker.com/_/mysql) image:

```shell
docker run --name=mysql \
-e MYSQL_USER=<DB_USER> \
-e MYSQL_PASSWORD=<DB_PASSWORD> \
-e MYSQL_DATABASE=<DB_DATABASE> \
-e MYSQL_RANDOM_ROOT_PASSWORD=true \
-p <DB_PORT>:3306 \
-d mysql:latest
```

or follow the official [installation instructions](https://dev.mysql.com/doc/mysql-getting-started/en/#mysql-getting-started-installing) to install and run
a MySQL instance.

### Configure database access

Open the `config.yaml` file in the `backend/config` or create your own `*.yaml` file and add the following:

```yaml
database:
  user: <DB_USER>
  password: <DB_PASSWORD>
  host: localhost # change this if the DB is not running on localhost, esp. in a production setting
  port: <DB_PORT>
  database: <DB_DATABASE>
  dialect: <DB_DIALECT> # depending on your choice of DB: postgres, mysql
```

Replace `<DB_USER>`, `<DB_PASSWORD>`, `<DB_PORT>`, `<DB_DATABASE>` with the values used in your running
DB instance (cf. the Docker commands above used for running the DB containers) and replace `<DB_DIALECT>` with
the DB of your choice.

### Apply Database migrations

Before you can start and use the service you need to run the database migrations:

#### Docker

```shell
docker run --mount type=bind,source=<PATH-TO-CONFIG-FILE>,target=/config/config.yaml -p 8000:8000 -it ghcr.io/teamhanko/hanko:latest migrate up
```

> **Note** The `<PATH-TO-CONFIG-FILE>` must be an absolute path to your config file created above.

#### From source

First build the Hanko backend. The only prerequisite is to have Go (v1.18+) [installed](https://go.dev/doc/install)
on your computer.

```shell
go generate ./...
go build -a -o hanko main.go
```

This command will create an executable with the name `hanko`, which then can be used to apply the database migrations
and start the Hanko backend.

To apply the migrations, run:

```shell
./hanko migrate up --config <PATH-TO-CONFIG-FILE>
```

> **Note** The path to the config file can be relative or absolute.


### Run and configure an SMTP server

The Hanko backend requires an SMTP server to send out mails containing
passcodes (e.g. for the purpose of email verification, password recovery).

For local development purposes you can use, e.g., [Mailslurper](https://www.mailslurper.com/).
Follow the official [installation](https://github.com/mailslurper/mailslurper/wiki/Getting-Started) instructions or
use an (inofficial) [Docker image](https://hub.docker.com/r/marcopas/docker-mailslurper) to get it up and running:

```shell
docker run --name=mailslurper -it -p 2500:2500 -p 8080:8080 -p 8085:8085 @marcopas/docker-mailslurper
```

where in this case
- `2500` is the SMTP port of the service
- `8080` is the port for the GUI application for managing mails
- `8085` is the port for the [API](https://github.com/mailslurper/mailslurper/wiki/API-Guide) service for managing mails

When using the above Docker command to run a Mailslurper container, it does not configure
a user/password, so a minimal configuration in your configuration file (`backend/config/config.yaml` or
your own `*.yaml` file) could contain the following:

```yaml
email_delivery:
  enabled: true
  email:
    from_address: no-reply@example.com
    from_name: Example Application
  smtp:
    host: localhost
    port: 2500
```

To ensure that passcode emails also contain a proper subject header, configure a service
name:

```yaml
service:
  name: Example Authentication Service
```

In a production setting you would rather use a self-hosted SMTP server or a managed service like AWS SES. In that case
you need to supply the `email_delivery.smtp.host`, `email_delivery.smtp.port` as well as the `email_delivery.smtp.user`,
`email_delivery.smtp.password` settings according to your server/service settings.

### Configure JSON Web Key Set generation

The API uses [JSON Web Tokens](https://www.rfc-editor.org/rfc/rfc7519.html) (JWTs) for
[authentication](https://docs.hanko.io/api-reference/public/introduction).
JWTs are verified using [JSON Web Keys](https://www.rfc-editor.org/rfc/rfc7517) (JWK).
JWKs are created internally by setting `secrets.keys` options in the
configuration file (`backend/config/config.yaml` or your own `*.yaml` file):

```yaml
secrets:
  keys:
    - <CHANGE-ME>
```

> **Note**  at least one `secrets.keys` entry must be provided and each entry must be a random generated string at least 16 characters long.

Keys secrets are used to en- and decrypt the JWKs which get used to sign the JWTs.
For every key a JWK is generated, encrypted with the key and persisted in the database.

The Hanko backend API publishes public cryptographic keys as a JWK set through the `.well-known/jwks.json`
[endpoint](https://docs.hanko.io/api-reference/public/well-known/get-json-web-key-set) to enable clients to verify token
signatures.

### Configure WebAuthn

Passkeys are based on the [Web Authentication API](https://www.w3.org/TR/webauthn-2/#web-authentication-api).
In order to create and login with passkeys, the Hanko backend must be provided information about
the [WebAuthn Relying Party](https://www.w3.org/TR/webauthn-2/#webauthn-relying-party).

For most use cases, you just need the domain of your web application that uses the Hanko backend. Set
`webauthn.relying_party.id` to the domain and set `webauthn.relying_party.origin` to the domain _including_ the
protocol.

> **Important**: If you are hosting your web application on a non-standard HTTP port (i.e. `80`) you also have to
> include this in the origin setting.

#### Local development example

When developing locally, the Hanko backend defaults to:

```yaml
webauthn:
  relying_party:
    id: "localhost"
    display_name: "Hanko Authentication Service"
    origins:
      - "http://localhost"
```

so no further configuration changes need to be made to your configuration file.

#### Production Examples

When you have a website hosted at `example.com` and you want to add a login to it that will be available
at `https://example.com/login`, the WebAuthn config would look like this:

```yaml
webauthn:
  relying_party:
    id: "example.com"
    display_name: "Example Project"
    origins:
      - "https://example.com"
```

If the login should be available at `https://login.example.com` instead, then the WebAuthn config would look like this:

```yaml
webauthn:
  relying_party:
    id: "login.example.com"
    display_name: "Example Project"
    origins:
      - "https://login.example.com"
```

Given the above scenario, you still may want to bind your users WebAuthn credentials to `example.com` if you plan to
add other services on other subdomains later that should be able to use existing credentials. Another reason can be if
you want to have the option to move your login from `https://login.example.com` to `https://example.com/login` at some
point. Then the WebAuthn config would look like this:

```yaml
webauthn:
  relying_party:
    id: "example.com"
    display_name: "Example Project"
    origins:
      - "https://login.example.com"
```

### Configure CORS

Because the backend and your application(s) consuming backend API most likely have different origins, i.e.
scheme (protocol), hostname (domain), and port part of the URL are different, you need to configure
Cross-Origin Resource Sharing (CORS) and specify your application(s) as allowed origins:

```yaml
server:
  public:
    cors:
      allow_origins:
        - https://example.com
```

When you include a wildcard `*` origin you need to set `unsafe_wildcard_origin_allowed: true`:

```yaml
server:
  public:
    cors:
      allow_origins:
        - "*"
      unsafe_wildcard_origin_allowed: true
```

Wildcard `*` origins can lead to cross-site attacks and when you include a `*` wildcard origin,
we want to make sure, that you understand what you are doing, hence this flag.

> **Note** In most cases, the `allow_origins` list here should contain the same entries as the `webauthn.relying_party.origins` list. Only when you have an Android app you will have an extra entry (`android:apk-key-hash:...`) in the `webauthn.relying_party.origins` list.

### Start the backend

The Hanko backend consists of a public and an administrative API (currently providing user management
endpoints). These can be started separately or in a single command.

#### Start the public API

##### Docker

```shell
docker run --mount type=bind,source=<PATH-TO-CONFIG-FILE>,target=/config/config.yaml -p 8000:8000 -it ghcr.io/teamhanko/hanko:latest serve public
```

##### Using pre-built binaries

Each [GitHub release](https://github.com/teamhanko/hanko/releases) (> 0.9.0) has `hanko`'s binary assets uploaded to it. Alternatively you can use
a tool like [eget](https://github.com/zyedidia/eget) to install binaries from releases on GitHub:

```bash
eget teamhanko/hanko
```

##### From source

```shell
go generate ./...
go build -a -o hanko main.go
```

Then run:
```shell
./hanko serve public --config <PATH-TO-CONFIG-FILE>
```

> **Note** The `<PATH-TO-CONFIG-FILE>` must be an absolute path to your config file created above.

`8000` is the default port for the public API. It can
be [customized](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-public#address) in the
configuration through the `server.public.address` option.

The service is now available at `localhost:8000`.

#### Start the admin API

In the usage section above we only started the public API. Use the command below to start the admin API. The default
port is `8001`, but can be
[customized](https://github.com/teamhanko/hanko/wiki/hanko-properties-server-properties-admin) in the configuration
through the `server.admin.address` option.

```shell
serve admin
```

> **Warning** The admin API must be protected by an access management system.

##### Start both public and admin API

Use this command to start the public and admin API together:

```shell
serve all
```

## Running tests

You can run the unit tests by running the following command within the `backend` directory:

```bash
go test -v ./...
```

## Additional topics

### Enabling password authentication

Password-based authentication is disabled per default. You can activate it and set the minimum password
length in your configuration file:

```yaml
password:
  enabled: true
  min_password_length: 8
```

### Cross-domain communication

JWTs used for authentication are propagated via cookie. If your application and the Hanko backend run on different
domains, cookies cannot be set by the Hanko backend. In that case the backend must be configured to transmit the JWT via
Header (`X-Auth-Token`). To do so, enable propagation of the `X-Auth-Token` header:

```yaml
session:
  enable_auth_token_header: true
```

### Audit logs

API operations are recorded in an audit log. By default, the audit log is enabled
and logs to STDOUT:

```yaml
audit_log:
  console_output:
    enabled: true
    output: "stdout"
  storage:
    enabled: false
```

To persist audit logs in the database, set `audit_log.storage.enabled` to `true`.

### Rate Limiting

Hanko implements basic fixed-window rate limiting for the passcode/init and password/login endpoints to mitigate brute-force attacks.
It uses a combination of user-id/IP to mitigate DoS attacks on user accounts. You can choose between an in-memory and a redis store.

In production systems, you may want to hide the
Hanko service behind a proxy or gateway (e.g. Kong, Traefik) to provide additional network-based rate limiting.

### Social connections

Hanko supports OAuth-based ([authorization code flow](https://www.rfc-editor.org/rfc/rfc6749#section-1.3.1)) third
party provider logins. The `third_party` configuration
[option](https://github.com/teamhanko/hanko/wiki/config-properties-third_party) contains all relevant configuration.
This includes options for setting up redirect URLs (in case of success or error on authentication with a provider) that
apply to both [built-in](#built-in-providers) and
[custom](#custom-oauthoidc-providers) providers.


#### Built-in providers

Built-in providers can be configured through the `third_party.providers` configuration [option](https://github.com/teamhanko/hanko/wiki/config-properties-third_party).
They must be explicitly `enabled` (i.e. providers are disabled default).
All provider configurations require provider credentials in the form of a client ID (`client_id`)
and a client secret (`secret`). See the guides in the official documentation for instructions on how to obtain these:

- [Apple](https://docs.hanko.io/guides/authentication-methods/oauth/apple)
- [Discord](https://docs.hanko.io/guides/authentication-methods/oauth/discord)
- [GitHub](https://docs.hanko.io/guides/authentication-methods/oauth/github)
- [Google](https://docs.hanko.io/guides/authentication-methods/oauth/google)
- [LinkedIn](https://docs.hanko.io/guides/authentication-methods/oauth/linkedin)
- [Microsoft](https://docs.hanko.io/guides/authentication-methods/oauth/microsoft)

#### Custom OAuth/OIDC providers

Custom providers can be configured through the `third_party.custom_providers` configuration
[option](https://github.com/teamhanko/hanko/wiki/config-properties-third_party-properties-custom_providers).
Like built-in providers they must be explicitly `enabled` and require a `client_id` and `secret`, which must
be obtained from the respective provider.
Custom providers can use either OAuth or OIDC. OIDC providers can be configured to use
[OIDC Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) by setting the `use_discovery`
option to `true`. An `issuer` must be configured too in that case. Otherwise both OAuth and OIDC providers
can manually define required endpoints (`authorization_endpoint`, `token_endpoint`, `userinfo_endpoint`).
`scopes` must be explicitly defined (with `openid` being the minimum requirement in case of OIDC providers).

#### Account linking

The `allow_linking` configuration option for built-in and custom providers determines whether automatic account linking for this provider
is activated. Note that account linking is based on e-mail addresses and OAuth providers may allow account holders to
use unverified e-mail addresses or may not provide any information at all about the verification status of e-mail
addresses. This poses a security risk and potentially allows bad actors to hijack existing Hanko
accounts associated with the same address. It is therefore recommended to make sure you trust the provider and to
also enable `emails.require_verification` in your configuration to ensure that only verified third party provider
addresses may be used.

### User metadata

Hanko allows for defining arbitrary user metadata. Metadata can be categorized into
three types that differ as to how they can be accessed and modified:

| Metadata type | Public API                   | Admin API             |
|---------------|------------------------------|-----------------------|
| Private       | No read or write access      | Read and write access |
| Public        | Read access                  | Read and write access |
| Unsafe        | Read access and write access | Read and write access |

Each metadata type supports a maximum of 3,000 characters. Metadata is stored as compact JSON (whitespace is ignored).
JSON syntax characters (`{`, `:`, `"`, `}`) count toward the character limit.
Multibyte UTF-8 characters (like emojis or non-Latin characters) count as 1 character each.

#### Private metadata

Private metadata should be used for sensitive data that should not be exposed to the client (e.g., internal flags/ids,
configuration, or access control details).

Private metadata can be read through the Admin API only using the
[Get metadata of a user](/api-reference/admin/user-management/get-metadata-of-a-user)
endpoint.

Private metadata can be set and modified through the Admin API only by using the
[Patch metadata of a user](https://docs.hanko.io/api-reference/admin/user-management/patch-metadata-of-a-user) endpoint.

#### Public metadata

Public metadata should be used for non-sensitive information that you want accessible but not modifiable by the client
(e.g., certain user roles, UI preferences, display options).

Public metadata can be read through the Public API, the Admin API and in JWT templates for customizing
the session JWT:

- `Public API`:
  - Public metadata is returned in the `user` object in the payload on the `success` state in a
    [Login](https://docs.hanko.io/api-reference/flow/login) and
    [Registration](https://docs.hanko.io/api-reference/flow/registration) flow as well
    as in the payload on the `profile_init` state in a [Profile](https://docs.hanko.io/api-reference/flow/profile) flow.
  - Public metadata is returned as part of the response of the
    [Get a user by ID](https://docs.hanko.io/api-reference/public/user-management/get-a-user-by-id) endpoint.
- `Admin API`:
  - Public metadata is returned as part of the response of the
    [Get metadata of a user](https://docs.hanko.io/api-reference/admin/user-metadata-management/get-metadata-of-a-user)
    endpoint.
  - Public metadata is returned as part of the response of the
    [Get a user by ID](https://docs.hanko.io/api-reference/admin/user-management/get-a-user-by-id) endpoint.
- `JWT Templates`:
  - Public metadata can be accessed through the `User` context object available on session JWT customization.
    See [Session JWT templates](#session-jwt-templates) for more details.

Public metadata can be set and modified through the Admin API only by using the
[Patch metadata of a user](https://docs.hanko.io/api-reference/admin/user-management/patch-metadata-of-a-user) endpoint.

#### Unsafe metadata

Unsafe metadata should be used for non-sensitive, temporary or experimental data that doesn't need strong safety
guarantees.

Unsafe metadata can be read through the Public API, the Admin API and in JWT templates for customizing
the session JWT:

- `Public API`:
    - Unsafe metadata is returned in the `user` object in the payload on the `success` state in a
      [Login](https://docs.hanko.io/api-reference/flow/login) and
      [Registration](https://docs.hanko.io/api-reference/flow/registration) flow as well
      as in the payload on the `profile_init` state in a [Profile](https://docs.hanko.io/api-reference/flow/profile) flow.
    - Unsafe metadata is returned as part of the response of the
      [Get a user by ID](https://docs.hanko.io/api-reference/public/user-management/get-a-user-by-id) endpoint.
- `Admin API`:
    - Unsafe metadata is returned as part of the response of the
      [Get metadata of a user](https://docs.hanko.io/api-reference/admin/user-metadata-management/get-metadata-of-a-user)
      endpoint.
    - Unsafe metadata is returned as part of the response of the
      [Get a user by ID](https://docs.hanko.io/api-reference/admin/user-management/get-a-user-by-id) endpoint.
- `JWT Templates`:
    - Unsafe metadata can be accessed through the `User` context object available on session JWT customization.
      See [Session JWT templates](#session-jwt-templates) for more details.

Unsafe metadata can be set and modified through the Public API and the Admin API:

- `Public API`:
  - Unsafe metadata can be set using the `patch_metadata` action in the
    [Profile](https://docs.hanko.io/api-reference/flow/profile) flow.

- `Admin API`:
  - Unsafe metadata can be set using the
    [Patch metadata of a user](https://docs.hanko.io/api-reference/admin/user-management/patch-metadata-of-a-user)
    endpoint.



### User import
You can import an existing user pool into Hanko using json in the following format:
```json
[
  {
    "user_id": "799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3",
    "emails": [
      {
        "address": "koreyrath@wolff.name",
        "is_primary": true,
        "is_verified": true
      }
    ],
    "created_at": "2023-06-07T13:42:49.369489Z",
    "updated_at": "2023-06-07T13:42:49.369489Z"
  },
  {
    "user_id": "",
    "emails": [
      {
        "address": "joshuagrimes@langworth.name",
        "is_primary": true,
        "is_verified": true
      }
    ],
    "created_at": "2023-06-07T13:42:49.369494Z",
    "updated_at": "2023-06-07T13:42:49.369494Z"
  }
]
```
There is a json schema file located [here](json_schema/hanko.user_import.json) that you can use for validation and input suggestions.
To import users run:

> hanko user import -i ./path/to/import_file.json


### Webhooks

Webhooks are an easy way to get informed about changes in your Hanko instance (e.g. user or email updates).
To use webhooks you have to provide an endpoint on your application which can process the events. Please be aware that your
endpoint need to respond with an HTTP status code 200. Else-wise the delivery of the event will not be counted as successful.

#### Events
When a webhook is triggered it will send you a **JSON** body which contains the event and a jwt.
The JWT contains 2 custom claims:

* **data**: contains the whole object for which the change was made. (e.g.: the whole user object when an email or user is changed/created/deleted)
* **evt**: the event for which the webhook was triggered

A typical webhook event looks like:

```json
{
  "token": "the-jwt-token-which-contains-the-data",
  "event": "name of the event"
}
```

To decode the webhook you can use the JWKs created in [Configure JSON Web Key Set generation](#configure-json-web-key-set-generation)

#### Event Types

Hanko sends webhooks for the following event types:

| Event                       | Triggers on                                                                                        |
|-----------------------------|----------------------------------------------------------------------------------------------------|
| user                        | user creation, user deletion, user update, email creation, email deletion, change of primary email |
| user.create                 | user creation                                                                                      |
| user.delete                 | user deletion                                                                                      |
| user.login                  | user login                                                                                         |
| user.update                 | user update, email creation, email deletion, change of primary email                               |
| user.update.email           | email creation, email deletion, change of primary email                                            |
| user.update.email.create    | email creation                                                                                     |
| user.update.email.delete    | email deletion                                                                                     |
| user.update.email.primary   | change of primary email                                                                            |
| user.update.username.create | username creation                                                                                  |
| user.update.username.delete | username deletion                                                                                  |
| user.update.username.update | change of username                                                                                 |
| email.send                  | an email was sent or should be sent                                                                |

As you can see, events can have subevents. You are able to filter which events you want to receive by either selecting
a parent event when you want to receive all subevents or selecting specific subevents.

#### Enabling Webhooks

You can activate webhooks by adding the following snippet to your configuration file:

```yaml
webhooks:
  enabled: true
  hooks:
    - callback: <YOUR WEBHOOK ENDPOINT>
      events:
        - user
```

### Session JWT templates

You can define custom claims that will be added to session JWTs through the `session.jwt_template.claims`
configuration option.

These claims are processed at JWT generation time and can include static values,
templated strings using Go's text/template syntax, or nested structures (maps and slices).

The template has access to user data via the `.User` field, which includes:
- `.User.UserID`: The user's unique ID (string)
- `.User.Email`: Email details (optional)
  - `User.Email.Address`: The actual email address
  - `User.Email.IsPrimary`: Whether this email address is the primary email address of this user
  - `User.Email.IsVerified`: Whether this email address has been verified by the user
- `.User.FamilyName`: The user's family name (string, optional)
- `.User.GivenName`: The user's given name (string, optional)
- `.User.Name`: The user's full name (string, optional)
- `.User.Picture`: The user's profile picture URL (string, optional)
- `.User.Username`: The user's username (string, optional)
- `.User.Metadata`: The user's public and unsafe metadata (optional)
    - `.User.Metadata.Public`: The user's public metadata (object)
    - `.User.Metadata.Unsafe`: The user's unsafe metadata (object)

#### Accessing user metadata

`.User.Metadata.Public` and `.User.Metadata.Unsafe`  can be accessed and queried using
[GJSON Path Syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md) (try it out in the
[playground](https://gjson.dev/)).

Assume that a user's public metadata consisted of the following data:

```json
{
    "display_name": "GamerDude",
    "favorite_games": [
        {
            "name": "Legends of Valor",
            "genre": "RPG",
            "playtime_hours": 142.3
        },
        {
            "name": "Space Raiders",
            "genre": "Sci-Fi Shooter",
            "playtime_hours": 87.6
        }
    ]
}
```

Then you could, for example, access this data in the following ways in your templates:

```yaml
display_name: '{{ .User.Metadata.Public "display_name" }}'
favorite_games: '{{ .User.Metadata.Public "favorite_games" }}'
favorite_games_with_playtime_over_100: '{{ .User.Metadata.Public "favorite_games.#(playtime_hours>100)" }}'
favorite_genres: '{{ .User.Metadata.Public "favorite_games.#.genre" }}'
```

> **Note**
>
> Ensure you use proper quoting when accessing metadata. `.User.Metadata.Public` and `.User.Metadata.Unsafe`
are function calls internally and the given path argument must be a string, so it must be double quoted.
If you use use double quotes for your entire claim template then the path argument must be escaped, i.e.:
`"{{ .User.Metadata.Public \"display_name\" }}"`


Example usage in YAML configuration:
```yaml
role: "user"                                           # Static value
user_email: "{{.User.Email.Address}}"                  # Templated string
is_verified: "{{.User.Email.IsVerified}}"              # Boolean from user data
metadata:                                              # Nested map
  greeting: "Hello {{.User.Username}}"
  source: '{{ .User.Metadata.Public "display_name" }}' # Data read from public metadata
  ui_theme: '{{ .User.Metadata.Unsafe "ui_theme" }}'   # Data read from unsafe metadata
scopes:                                                # Slice with templated value
    - "read"
    - "write"
    - "{{if .User.Email.IsVerified}}admin{{else}}basic{{end}}"
```

In this example:
- `role` is a static string ("user").
- `user_email` dynamically inserts the user's email address.
- `is_verified` inserts a boolean indicating email verification status.
- `metadata` is a nested map with a static `source` and a templated `greeting`.
- `scopes` is a slice combining static values and a conditional template.

Notes:
- Custom claims are added at the top level of the session token [payload](#jwt-payload).
- Claims with the following keys will be ignored because they are currently added to the JWT by default:
    - `sub`
    - `iat`
    - `exp`
    - `aud`
    - `iss`
    - `email`
    - `username`
    - `session_id`
- Templates must conform to valid [Go text/template syntax](https://pkg.go.dev/text/template). Invalid templates are
  logged and excluded from the generated token.
- Boolean strings ("true" or "false") from templates are automatically converted to actual booleans.

For more details on template syntax, see: https://pkg.go.dev/text/template

## API specification

- [Hanko Public API](https://docs.hanko.io/api-reference/public/introduction)
- [Hanko Admin API](https://docs.hanko.io/api-reference/admin/introduction)

## Configuration reference

- [Using configuration file](https://github.com/teamhanko/hanko/wiki/Using-configuration-file)
- [Using environment variables](https://github.com/teamhanko/hanko/wiki/Using-environment-variables)
- [Configuration reference](https://github.com/teamhanko/hanko/wiki/config)


## License

The Hanko backend ist licensed under the [AGPL-3.0](../LICENSE).


================================================
FILE: backend/audit_log/logger.go
================================================
package auditlog

import (
	"github.com/gobuffalo/pop/v6"
	"github.com/labstack/echo/v4"
	zeroLog "github.com/rs/zerolog"
	zeroLogger "github.com/rs/zerolog/log"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"github.com/teamhanko/hanko/backend/v2/persistence/models"
	"github.com/teamhanko/hanko/backend/v2/utils"
	"os"
	"strconv"
	"time"
)

type Logger interface {
	Create(echo.Context, models.AuditLogType, *models.User, error, ...DetailOption) error
	CreateWithConnection(*pop.Connection, echo.Context, models.AuditLogType, *models.User, error, ...DetailOption) error
}

type logger struct {
	persister             persistence.Persister
	storageEnabled        bool
	logger                zeroLog.Logger
	consoleLoggingEnabled bool
	mustMask              bool
}

func NewLogger(persister persistence.Persister, cfg config.AuditLog) Logger {
	var loggerOutput *os.File = nil
	switch cfg.ConsoleOutput.OutputStream {
	case config.OutputStreamStdOut:
		loggerOutput = os.Stdout
	case config.OutputStreamStdErr:
		loggerOutput = os.Stderr
	default:
		loggerOutput = os.Stdout
	}

	return &logger{
		persister:             persister,
		storageEnabled:        cfg.Storage.Enabled,
		logger:                zeroLog.New(loggerOutput),
		consoleLoggingEnabled: cfg.ConsoleOutput.Enabled,
		mustMask:              cfg.Mask,
	}
}

type DetailOption func(map[string]interface{})

func Detail(key string, value interface{}) DetailOption {
	return func(d map[string]interface{}) {
		if value != "" || value != nil {
			d[key] = value
		}
	}
}

func (l *logger) Create(context echo.Context, auditLogType models.AuditLogType, user *models.User, logError error, detailOpts ...DetailOption) error {
	return l.CreateWithConnection(l.persister.GetConnection(), context, auditLogType, user, logError, detailOpts...)
}

func (l *logger) CreateWithConnection(tx *pop.Connection, context echo.Context, auditLogType models.AuditLogType, user *models.User, logError error, detailOpts ...DetailOption) error {
	details := make(map[string]interface{})
	for _, detailOpt := range detailOpts {
		detailOpt(details)
	}

	auditLog, err := models.NewAuditLog(auditLogType, l.getRequestMeta(context), details, user, logError)
	if err != nil {
		return err
	}

	if l.mustMask {
		auditLog = l.mask(auditLog)
	}

	if l.storageEnabled {
		err = l.store(tx, auditLog)
		if err != nil {
			return err
		}
	}

	if l.consoleLoggingEnabled {
		l.logToConsole(auditLog)
	}

	return nil
}

func (l *logger) store(tx *pop.Connection, auditLog models.AuditLog) error {
	return l.persister.GetAuditLogPersisterWithConnection(tx).Create(auditLog)
}

func (l *logger) logToConsole(auditLog models.AuditLog) {
	var err string
	if auditLog.Error != nil {
		err = *auditLog.Error
	}

	now := time.Now()
	loggerEvent := zeroLogger.Log().
		Str("audience", "audit").
		Str("type", string(auditLog.Type)).
		Str("error", err).
		Str("http_request_id", auditLog.MetaHttpRequestId).
		Str("source_ip", auditLog.MetaSourceIp).
		Str("user_agent", auditLog.MetaUserAgent).
		Any("details", auditLog.Details).
		Str("time", now.Format(time.RFC3339Nano)).
		Str("time_unix", strconv.FormatInt(now.Unix(), 10))

	if auditLog.ActorUserId != nil {
		loggerEvent.Str("user_id", auditLog.ActorUserId.String())
		if auditLog.ActorEmail != nil {
			loggerEvent.Str("user_email", *auditLog.ActorEmail)
		}
	}

	loggerEvent.Send()
}

func (l *logger) getRequestMeta(c echo.Context) models.RequestMeta {
	return models.RequestMeta{
		HttpRequestId: c.Response().Header().Get(echo.HeaderXRequestID),
		UserAgent:     c.Request().UserAgent(),
		SourceIp:      c.RealIP(),
	}
}

func (l *logger) mask(auditLog models.AuditLog) models.AuditLog {
	if auditLog.ActorEmail != nil && *auditLog.ActorEmail != "" {
		email := utils.MaskEmail(*auditLog.ActorEmail)
		auditLog.ActorEmail = &email
	}

	for key, value := range auditLog.Details {
		if key == "username" {
			auditLog.Details[key] = utils.MaskUsername(value.(string))
		}

		if key == "email" {
			auditLog.Details[key] = utils.MaskEmail(value.(string))
		}
	}

	return auditLog
}


================================================
FILE: backend/build_info/build_info.go
================================================
package build_info

import (
	_ "embed"
	"runtime/debug"
	"strings"
)

//go:generate sh -c "git describe --tags --always --match backend/* | sed -e s#^backend/## > version.txt"
//go:embed version.txt
var version string
var realVersion *string
var isDirty *bool

func GetVersion() string {
	if realVersion == nil {
		tempVersion := strings.TrimSpace(version)
		if getIsDirty() {
			tempVersion += "-dirty"
		}
		realVersion = &tempVersion
	}
	return *realVersion
}

func getIsDirty() bool {
	if isDirty == nil {
		bi, ok := debug.ReadBuildInfo()
		if ok {
			modified := false
			for _, v := range bi.Settings {
				if v.Key == "vcs.modified" {
					if v.Value == "true" {
						modified = true
					}
				}
			}
			isDirty = &modified
		}
	}
	return *isDirty
}


================================================
FILE: backend/cmd/cleanup/cleanup.go
================================================
package cleanup

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"github.com/teamhanko/hanko/backend/v2/persistence/models"
	"log"
	"sort"
	"strings"
	"time"
)

// options holds user-provided CLI options
type options struct {
	tables     []string // List of tables to clean up
	configFile string   // Path to configuration file
	pageSize   int      // The number of entities to query at once
	run        bool     // Whether to execute cleanup or simulate
}

// handlerParam holds the necessary parameters for cleanup operations
type handlerParam struct {
	table   string
	config  *config.Config
	storage persistence.Storage
	options *options
}

// handlerFunc defines the function signature for cleanup handlers
type handlerFunc func(handlerParam) error

// Table names used for cleanup operations
const (
	tableAuditLogs           = "audit_logs"
	tableFlows               = "flows"
	tableWebauthnSessionData = "webauthn_session_data"
)

// Map of table names to their respective cleanup handlers
var handler = map[string]handlerFunc{
	tableFlows: func(param handlerParam) error {
		return cleanup[models.Flow](param, param.storage.GetFlowPersister(), time.Now().UTC())
	},
	tableAuditLogs: func(param handlerParam) error {
		duration, err := time.ParseDuration(param.config.AuditLog.Retention)
		if err != nil {
			return fmt.Errorf("failed to parse the retention duration: %w", err)
		}

		return cleanup[models.AuditLog](param, param.storage.GetAuditLogPersister(), time.Now().Add(-duration).UTC())
	},
	tableWebauthnSessionData: func(param handlerParam) error {
		return cleanup[models.WebauthnSessionData](param, param.storage.GetWebauthnSessionDataPersister(), time.Now().UTC())
	},
}

// allowedTables is a list of table names that can be cleaned up
var allowedTables = func() []string {
	keys := make([]string, 0, len(handler))
	for key := range handler {
		keys = append(keys, key)
	}

	sort.Strings(keys)

	return keys
}()

// isTableAllowed checks if a given table name exists in the allowed list
func isTableAllowed(table string) bool {
	for _, allowed := range allowedTables {
		if table == allowed {
			return true
		}
	}
	return false
}

// validateTables checks if the specified table names exist in the allowed list
func validateTables(tables []string) error {
	var invalidTables []string

	for _, table := range tables {
		if !isTableAllowed(table) {
			invalidTables = append(invalidTables, table)
		}
	}

	if len(invalidTables) > 0 {
		return fmt.Errorf("invalid table name(s): %s - allowed values: %s",
			strings.Join(invalidTables, ", "), strings.Join(allowedTables, ", "))
	}

	return nil
}

// newCleanupCommand creates the Cobra command for database cleanup
func newCleanupCommand() *cobra.Command {
	opts := &options{}

	cmd := &cobra.Command{
		Use:   "cleanup",
		Short: "Cleanup the database.",
		Long:  `Cleans up the database by deleting expired entities.`,
		PreRunE: func(cmd *cobra.Command, args []string) error {
			if len(opts.tables) == 0 {
				opts.tables = allowedTables
				return nil
			}

			return validateTables(opts.tables)
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			cfg, err := config.Load(&opts.configFile)
			if err != nil {
				log.Fatal(err)
			}

			storage, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}

			log.Printf("Cleaning up table(s): %s...\n", strings.Join(opts.tables, ", "))

			for _, table := range opts.tables {
				param := handlerParam{
					table:   table,
					config:  cfg,
					storage: storage,
					options: opts,
				}
				err = handler[table](param)
				if err != nil {
					log.Fatal(err)
				}
			}

			log.Println("Cleanup completed.")

			if !opts.run {
				log.Println("This was a dry-run; add --run to the command to really delete the data.")
			}

			return nil
		},
	}

	cmd.Flags().StringVarP(&opts.configFile, "config", "c", config.DefaultConfigFilePath, "path to config file")
	cmd.Flags().StringSliceVarP(&opts.tables, "tables", "t", []string{}, fmt.Sprintf("specify individual tables to clean up (comma-separated) - allowed values: %s", strings.Join(allowedTables, ", ")))
	cmd.Flags().IntVarP(&opts.pageSize, "page-size", "s", 512, "the number of entities to query at once")
	cmd.Flags().BoolVar(&opts.run, "run", false, "execute the cleanup process instead of simulating")

	return cmd
}

// cleanup performs the cleanup operation for a given table and persister
func cleanup[T any](param handlerParam, persister persistence.Cleanup[T], cutoffTime time.Time) error {
	var (
		page    = 1
		deleted = 0
	)

	for {
		items, err := persister.FindExpired(cutoffTime, page, param.options.pageSize)
		if err != nil {
			return err
		}

		if len(items) > 0 {
			for _, item := range items {
				if param.options.run {
					err = persister.Delete(item)
					if err != nil {
						return err
					}
				}

				deleted++
			}

			log.Printf("Deleted %d %s in total.", deleted, param.table)

			if !param.options.run {
				page++
			}
		}

		if len(items) < param.options.pageSize {
			break
		}
	}

	return nil
}

// RegisterCommands registers the cleanup command with the parent command
func RegisterCommands(parent *cobra.Command) {
	cmd := newCleanupCommand()
	parent.AddCommand(cmd)
}


================================================
FILE: backend/cmd/isready/isready.go
================================================
package isready

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"log"
	"net"
	"net/http"
)

func NewIsReadyCommand() *cobra.Command {
	var (
		configFile string
	)

	cmd := &cobra.Command{
		Use:       "isready",
		Args:      cobra.OnlyValidArgs,
		ValidArgs: []string{"admin", "public"},
		Short:     "Health check a service",
		Long: `Checks if the specified service is healthy. Possible values are "admin" and "public".
Uses the "/health/ready" endpoint to check if the service is ready to serve requests.
		`,
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) != 1 {
				log.Fatalf("Please specify a service to check")
			}
			service := args[0]

			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}

			address := ""
			if service == "admin" {
				address = cfg.Server.Admin.Address
			}
			if service == "public" {
				address = cfg.Server.Public.Address
			}
			host, port, err := net.SplitHostPort(address)
			if err != nil {
				log.Fatalf("Could not parse address %s", address)
			}
			if host == "" {
				host = "localhost"
			}
			requestUrl := fmt.Sprintf("http://%s:%s/health/ready", host, port)
			res, err := http.Get(requestUrl)
			if err != nil {
				log.Fatalf("Service %s is not ready", service)
			} else {
				if res.StatusCode != 200 {
					log.Fatalf("Service %s is not ready", service)
				} else {
					log.Println(fmt.Sprintf("Service %s is ready", service))
				}
			}
		},
	}

	cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")

	return cmd
}

func RegisterCommands(parent *cobra.Command) {
	cmd := NewIsReadyCommand()
	parent.AddCommand(cmd)
}


================================================
FILE: backend/cmd/jwk/create.go
================================================
package jwk

import (
	"encoding/json"
	"fmt"

	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/crypto/jwk/local_db"

	"log"
)

func NewCreateCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "create",
		Short: "create JSON Web Key and print them in the console",
		Long:  ``,
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("create called")
			generator := local_db.RSAKeyGenerator{}
			key, err := generator.Generate("key1")
			if err != nil {
				log.Panicln(err)
			}
			j, err := json.Marshal(key)
			if err != nil {
				log.Panicln(err)
			}
			fmt.Println(string(j))
		},
	}
	return cmd
}


================================================
FILE: backend/cmd/jwk/root.go
================================================
package jwk

import (
	"github.com/spf13/cobra"
)


func NewMigrateCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "jwk",
		Short: "Tools for handling JSON Web Keys",
		Long:  ``,
	}
}

func RegisterCommands(parent *cobra.Command) {
	cmd := NewMigrateCmd()
	parent.AddCommand(cmd)
	cmd.AddCommand(NewCreateCommand())
}


================================================
FILE: backend/cmd/jwt/create.go
================================================
package jwt

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"

	"github.com/gofrs/uuid"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/crypto/jwk"
	"github.com/teamhanko/hanko/backend/v2/dto"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"github.com/teamhanko/hanko/backend/v2/persistence/models"
	"github.com/teamhanko/hanko/backend/v2/session"
)

func NewCreateCommand() *cobra.Command {
	var (
		configFile string
		pretty     bool
	)

	cmd := &cobra.Command{
		Use:   "create [user_id]",
		Short: "generate a JSON Web Token for a given user_id",
		Long:  ``,
		Args: func(cmd *cobra.Command, args []string) error {
			if len(args) < 1 {
				return errors.New("user_id required")
			}
			if _, err := uuid.FromString(args[0]); err != nil {
				return errors.New("user_id is not a uuid")
			}
			return nil
		},
		Run: func(cmd *cobra.Command, args []string) {
			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}
			persister, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}
			jwkManager, err := jwk.NewManager(cfg.Secrets, persister)
			if err != nil {
				fmt.Printf("failed to create jwk persister: %s", err)
				return
			}

			sessionManager, err := session.NewManager(jwkManager, *cfg)
			if err != nil {
				fmt.Printf("failed to create session generator: %s", err)
				return
			}

			userId := uuid.FromStringOrNil(args[0])

			userModel, err := persister.GetUserPersister().Get(userId)
			if err != nil {
				fmt.Printf("failed to get user from db: %s", err)
				return
			}

			token, rawToken, err := sessionManager.GenerateJWT(dto.UserJWTFromUserModel(userModel))
			if err != nil {
				fmt.Printf("failed to generate token: %s", err)
				return
			}

			sessionID, _ := rawToken.Get("session_id")

			expirationTime := rawToken.Expiration()
			sessionModel := models.Session{
				ID:        uuid.FromStringOrNil(sessionID.(string)),
				UserID:    userId,
				CreatedAt: rawToken.IssuedAt(),
				UpdatedAt: rawToken.IssuedAt(),
				ExpiresAt: &expirationTime,
				LastUsed:  rawToken.IssuedAt(),
			}

			err = persister.GetSessionPersister().Create(sessionModel)
			if err != nil {
				fmt.Printf("failed to store session: %s", err)
				return
			}

			fmt.Printf("Token: %s\n", token)

			if pretty {
				rawTokenMap, err := rawToken.AsMap(context.Background())
				if err != nil {
					fmt.Println("failed to get JWT payload as map:", err)
					return
				}
				payloadJSON, err := json.MarshalIndent(rawTokenMap, "", "  ")
				if err != nil {
					fmt.Println("failed to marshal JWT payload as JSON:", err)
				}
				fmt.Printf("JWT payload: %s\n", string(payloadJSON))
			}
		},
	}

	cmd.Flags().StringVar(&configFile, "config", "", "config file")
	cmd.Flags().BoolVar(&pretty, "pretty", true, "pretty print the JWT payload")

	return cmd
}


================================================
FILE: backend/cmd/jwt/root.go
================================================
package jwt

import (
	"github.com/spf13/cobra"
)

func NewJwtCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "jwt",
		Short: "Tools for handling JSON Web Tokens",
		Long:  ``,
	}
}

func RegisterCommands(parent *cobra.Command) {
	cmd := NewJwtCmd()
	parent.AddCommand(cmd)
	cmd.AddCommand(NewCreateCommand())
}


================================================
FILE: backend/cmd/migrate/down.go
================================================
package migrate

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"log"
	"strconv"
)

var steps int

func NewMigrateDownCommand() *cobra.Command {
	var (
		configFile string
	)

	cmd := &cobra.Command{
		Use:   "down",
		Short: "migrate the database down - given the number of steps",
		Long:  ``,
		Args: func(cmd *cobra.Command, args []string) error {
			var err error
			if steps, err = strconv.Atoi(args[0]); err != nil {
				return err
			}
			return nil
		},
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("migrate down called")

			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}

			persister, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}
			err = persister.MigrateDown(steps)
			if err != nil {
				log.Fatal(err)
			}
		},
	}

	cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")

	return cmd
}


================================================
FILE: backend/cmd/migrate/root.go
================================================
/*
Copyright © 2022 Hanko GmbH <developers@hanko.io>

*/

package migrate

import (
	"github.com/spf13/cobra"
)

//var persister *persistence.Persister

func NewMigrateCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "migrate",
		Short: "Database migration helpers",
		Long:  ``,
	}
}

func RegisterCommands(parent *cobra.Command) {
	cmd := NewMigrateCmd()
	parent.AddCommand(cmd)
	cmd.AddCommand(NewMigrateUpCommand())
	cmd.AddCommand(NewMigrateDownCommand())
}


================================================
FILE: backend/cmd/migrate/up.go
================================================
package migrate

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"log"
)

func NewMigrateUpCommand() *cobra.Command {
	var (
		configFile string
	)

	cmd := &cobra.Command{
		Use:   "up",
		Short: "migrate the database up",
		Long:  ``,
		Run: func(cmd *cobra.Command, args []string) {
			log.Println("migrate up")

			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}

			persister, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}
			err = persister.MigrateUp()
			if err != nil {
				log.Fatal(err)
			}

			err = persister.GetConnection().Close()
			if err != nil {
				log.Println(fmt.Errorf("failed to close db connection: %w", err))
			}
		},
	}

	cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")

	return cmd
}


================================================
FILE: backend/cmd/root.go
================================================
/*
Copyright © 2022 Hanko GmbH <developers@hanko.io>
*/
package cmd

import (
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/cmd/cleanup"
	"github.com/teamhanko/hanko/backend/v2/cmd/isready"
	"github.com/teamhanko/hanko/backend/v2/cmd/jwk"
	"github.com/teamhanko/hanko/backend/v2/cmd/jwt"
	"github.com/teamhanko/hanko/backend/v2/cmd/migrate"
	"github.com/teamhanko/hanko/backend/v2/cmd/schema"
	"github.com/teamhanko/hanko/backend/v2/cmd/serve"
	"github.com/teamhanko/hanko/backend/v2/cmd/siwa"
	"github.com/teamhanko/hanko/backend/v2/cmd/user"
	"github.com/teamhanko/hanko/backend/v2/cmd/version"
	"log"
)

func NewRootCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use: "hanko",
	}

	migrate.RegisterCommands(cmd)
	serve.RegisterCommands(cmd)
	isready.RegisterCommands(cmd)
	jwk.RegisterCommands(cmd)
	jwt.RegisterCommands(cmd)
	version.RegisterCommands(cmd)
	user.RegisterCommands(cmd)
	siwa.RegisterCommands(cmd)
	schema.RegisterCommands(cmd)
	cleanup.RegisterCommands(cmd)

	return cmd
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	cmd := NewRootCmd()

	err := cmd.Execute()
	if err != nil {
		log.Fatal(err)
	}
}


================================================
FILE: backend/cmd/schema/generate.go
================================================
package schema

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/invopop/jsonschema"
	"github.com/spf13/cobra"
	"log"
	"os"
	"path/filepath"
)

func NewGenerateCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "generate",
		Short: "Generate JSON Schema",
		Long:  ``,
	}

	cmd.AddCommand(NewGenerateImportCommand())
	cmd.AddCommand(NewGenerateConfigCommand())

	return cmd
}

type generateSchemaParams struct {
	structure       interface{}
	rootSchemaTitle string
	output          string
	doNotReference  bool
	extractComments bool
	commentPaths    []string
}

func generateSchema(params generateSchemaParams) error {
	r := new(jsonschema.Reflector)

	if params.doNotReference {
		r.DoNotReference = true
	}

	if params.extractComments {
		for _, path := range params.commentPaths {
			if err := r.AddGoComments("github.com/teamhanko/hanko/backend/v2", path); err != nil {
				return err
			}
		}
	}

	s := r.Reflect(params.structure)

	if params.rootSchemaTitle != "" {
		s.Title = params.rootSchemaTitle
	}

	data, err := json.MarshalIndent(s, "", "  ")
	if err != nil {
		return err
	}

	outPath, outFile := filepath.Split(params.output)

	if outPath != "" {
		_, err = os.Stat(outPath)
		if err != nil {
			if errors.Is(err, os.ErrNotExist) {
				log.Printf("directory %s does not exist, creating directory", outPath)

				mkDirErr := os.MkdirAll(outPath, 0750)

				if mkDirErr != nil {
					return fmt.Errorf("could not create directory %s: %w", outPath, mkDirErr)
				}
			} else {
				return fmt.Errorf("could not get file info: %w", err)
			}
		}
	}

	var destination string
	if outFile == "" {
		log.Println("no output file given, using default: output.schema.json")
		destination, err = filepath.Abs(filepath.Join(outPath, "output.schema.json"))
	} else {
		destination, err = filepath.Abs(filepath.Join(outPath, outFile))
	}

	if err != nil {
		return fmt.Errorf("could not determine file destination: %w", err)
	}

	err = os.WriteFile(destination, data, 0600)
	if err != nil {
		return fmt.Errorf("could not write file to destination %s: %w", destination, err)
	}

	log.Printf("schema generated successfully at: %s\n", destination)

	return nil
}


================================================
FILE: backend/cmd/schema/generate_config.go
================================================
package schema

import (
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"log"
)

func NewGenerateConfigCommand() *cobra.Command {
	var (
		output string
	)

	cmd := &cobra.Command{
		Use:   "config",
		Short: "Generate JSON schema for the backend config",
		Run: func(cmd *cobra.Command, args []string) {
			err := generateSchema(generateSchemaParams{
				structure:       &config.Config{},
				rootSchemaTitle: "Config",
				output:          output,
				extractComments: true,
				doNotReference:  false,
				commentPaths:    []string{"config", "ee"},
			})
			if err != nil {
				log.Fatal(err)
			}
		},
	}

	cmd.Flags().StringVarP(
		&output, "output",
		"o",
		"json_schema/hanko.config.json",
		"Output file")

	return cmd
}


================================================
FILE: backend/cmd/schema/generate_import.go
================================================
package schema

import (
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/cmd/user"
	"log"
)

func NewGenerateImportCommand() *cobra.Command {
	var (
		output string
	)

	cmd := &cobra.Command{
		Use:   "import",
		Short: "Generate JSON schema for the user import/export",
		Run: func(cmd *cobra.Command, args []string) {
			err := generateSchema(generateSchemaParams{
				structure:       &user.ImportOrExportList{},
				rootSchemaTitle: "User import",
				output:          output,
				extractComments: true,
				doNotReference:  false,
				commentPaths:    []string{"cmd/user"},
			})
			if err != nil {
				log.Fatal(err)
			}
		},
	}

	cmd.Flags().StringVarP(
		&output, "output",
		"o",
		"json_schema/hanko.user_import.json",
		"Output file")

	return cmd
}


================================================
FILE: backend/cmd/schema/markdown.go
================================================
package schema

import "github.com/spf13/cobra"

func NewMarkdownCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "markdown",
		Short: "Generate markdown from JSON Schema",
		Long:  ``,
	}

	cmd.AddCommand(NewMarkdownConfigCommand())
	cmd.AddCommand(NewMarkdownImportCommand())

	return cmd
}


================================================
FILE: backend/cmd/schema/markdown_config.go
================================================
package schema

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"log"
	"os/exec"
	"path/filepath"
)

func NewMarkdownConfigCommand() *cobra.Command {
	var (
		outPath string
	)

	cmd := &cobra.Command{
		Use:   "config",
		Short: "Generate markdown for the backend config",
		Run: func(cmd *cobra.Command, args []string) {
			err := generateSchema(generateSchemaParams{
				structure:       &config.Config{},
				rootSchemaTitle: "Config",
				output:          filepath.Join(outPath, "config.schema.json"),
				extractComments: true,
				doNotReference:  true,
				commentPaths:    []string{"config", "ee"},
			})

			out, err := exec.Command("npx",
				"@adobe/jsonschema2md",
				fmt.Sprintf("--input=%s", outPath),
				fmt.Sprintf("--out=%s", outPath),
				"--schema-extension=schema.json",
				"--example-format=yaml",
				"--header=false",
				"--skip=definedinfact",
				"--skip=typefact",
				"--schema-out=-",
				"--properties=format",
				"--no-readme=true").
				CombinedOutput()

			if err != nil {
				log.Fatal(err)
			}

			log.Println(string(out))

			outPathAbs, _ := filepath.Abs(outPath)
			log.Printf("successfully generated markdown from JSON schema at: %s\n", outPathAbs)
		},
	}

	cmd.Flags().StringVarP(
		&outPath, "out-path",
		"o",
		".generated/docs/config",
		"Output path")

	return cmd
}


================================================
FILE: backend/cmd/schema/markdown_import.go
================================================
package schema

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/cmd/user"
	"log"
	"os/exec"
	"path/filepath"
)

func NewMarkdownImportCommand() *cobra.Command {
	var (
		outPath string
	)

	cmd := &cobra.Command{
		Use:   "import",
		Short: "Generate markdown for the user import schema",
		Run: func(cmd *cobra.Command, args []string) {
			err := generateSchema(generateSchemaParams{
				structure:       &user.ImportOrExportList{},
				rootSchemaTitle: "User import",
				output:          filepath.Join(outPath, "user_import.schema.json"),
				extractComments: true,
				doNotReference:  true,
				commentPaths:    []string{"cmd/user"},
			})

			out, err := exec.Command("npx",
				"@adobe/jsonschema2md",
				fmt.Sprintf("--input=%s", outPath),
				fmt.Sprintf("--out=%s", outPath),
				"--schema-extension=schema.json",
				"--example-format=yaml",
				"--header=false",
				"--skip=definedinfact",
				"--skip=typefact",
				"--schema-out=-",
				"--properties=format",
				"--no-readme=true").
				CombinedOutput()

			if err != nil {
				log.Fatal(err)
			}

			log.Println(string(out))

			outPathAbs, _ := filepath.Abs(outPath)
			log.Printf("successfully generated markdown from JSON schema at: %s\n", outPathAbs)
		},
	}

	cmd.Flags().StringVarP(
		&outPath, "out-path",
		"o",
		".generated/docs/import",
		"Output path")

	return cmd
}


================================================
FILE: backend/cmd/schema/root.go
================================================
package schema

import (
	"github.com/spf13/cobra"
)

func NewSchemaCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "schema",
		Short: "JSONSchema related commands",
		Long:  ``,
	}
}

func RegisterCommands(parent *cobra.Command) {
	cmd := NewSchemaCommand()
	cmd.AddCommand(NewGenerateCommand())
	cmd.AddCommand(NewMarkdownCommand())
	parent.AddCommand(cmd)
}


================================================
FILE: backend/cmd/serve/admin.go
================================================
/*
Copyright © 2022 Hanko GmbH <developers@hanko.io>
*/
package serve

import (
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"github.com/teamhanko/hanko/backend/v2/server"
	"log"
	"sync"
)

func NewServeAdminCommand() *cobra.Command {
	var (
		configFile string
	)

	cmd := &cobra.Command{
		Use:   "admin",
		Short: "Start the admin portion of the hanko server",
		Long:  ``,
		Run: func(cmd *cobra.Command, args []string) {
			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}

			persister, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}
			var wg sync.WaitGroup
			wg.Add(1)

			go server.StartAdmin(cfg, &wg, persister, nil)

			wg.Wait()
		},
	}

	cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")

	return cmd
}


================================================
FILE: backend/cmd/serve/all.go
================================================
/*
Copyright © 2022 Hanko GmbH <developers@hanko.io>
*/
package serve

import (
	"github.com/labstack/echo-contrib/echoprometheus"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/mapper"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"github.com/teamhanko/hanko/backend/v2/server"
	"log"
	"sync"
)

func NewServeAllCommand() *cobra.Command {
	var (
		configFile                string
		authenticatorMetadataFile string
	)

	cmd := &cobra.Command{
		Use:   "all",
		Short: "Start the public and admin portion of the hanko server",
		Long:  ``,
		Run: func(cmd *cobra.Command, args []string) {
			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}

			authenticatorMetadata := mapper.LoadAuthenticatorMetadata(&authenticatorMetadataFile)

			persister, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}
			var wg sync.WaitGroup
			wg.Add(2)

			prometheus := echoprometheus.NewMiddleware("hanko")

			go server.StartPublic(cfg, &wg, persister, prometheus, authenticatorMetadata)
			go server.StartAdmin(cfg, &wg, persister, prometheus)

			wg.Wait()
		},
	}

	cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")
	cmd.Flags().StringVar(&authenticatorMetadataFile, "auth-meta", "", "authenticator metadata file")

	return cmd
}


================================================
FILE: backend/cmd/serve/public.go
================================================
/*
Copyright © 2022 Hanko GmbH <developers@hanko.io>
*/
package serve

import (
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/mapper"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"github.com/teamhanko/hanko/backend/v2/server"
	"log"
	"sync"
)

func NewServePublicCommand() *cobra.Command {
	var (
		configFile                string
		authenticatorMetadataFile string
	)

	cmd := &cobra.Command{
		Use:   "public",
		Short: "Start the public portion of the hanko server",
		Long:  ``,
		Run: func(cmd *cobra.Command, args []string) {
			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}

			authenticatorMetadata := mapper.LoadAuthenticatorMetadata(&authenticatorMetadataFile)

			persister, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}
			var wg sync.WaitGroup
			wg.Add(1)

			go server.StartPublic(cfg, &wg, persister, nil, authenticatorMetadata)

			wg.Wait()
		},
	}

	cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")
	cmd.Flags().StringVar(&authenticatorMetadataFile, "auth-meta", "", "authenticator metadata file")

	return cmd
}


================================================
FILE: backend/cmd/serve/root.go
================================================
/*
Copyright © 2022 Hanko GmbH <developers@hanko.io>

*/
package serve

import (
	"github.com/spf13/cobra"
)

func NewServeCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "serve",
		Short: "Start the hanko server",
		Long:  ``,
	}
}

func RegisterCommands(parent *cobra.Command) {
	cmd := NewServeCommand()
	parent.AddCommand(cmd)
	cmd.AddCommand(NewServePublicCommand())
	cmd.AddCommand(NewServeAdminCommand())
	cmd.AddCommand(NewServeAllCommand())
}


================================================
FILE: backend/cmd/siwa/siwa.go
================================================
package siwa

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"github.com/lestrrat-go/jwx/v2/jwa"
	"github.com/lestrrat-go/jwx/v2/jws"
	"github.com/lestrrat-go/jwx/v2/jwt"
	"github.com/spf13/cobra"
	"io/ioutil"
	"log"
	"path/filepath"
	"time"
)

func NewSignInWithAppleCommand() *cobra.Command {
	var (
		privateKey string
		teamID     string
		servicesID string
		keyID      string
	)

	cmd := &cobra.Command{
		Use:   "siwa",
		Short: "Generate a client secret (JWT) for Sign in with Apple (SIWA)",
		Long: `Sign in with Apple requires JWTs to authorize requests. This command creates the token,
then signs it with the private key obtained from the Apple Developer console.

See: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens#3262048`,
		Run: func(cmd *cobra.Command, args []string) {
			path := filepath.Clean(privateKey)
			bytes, err := ioutil.ReadFile(path)
			if err != nil {
				log.Fatal(err)
			}

			block, _ := pem.Decode(bytes)
			key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
			if err != nil {
				log.Fatal(err)
			}

			now := time.Now().UTC()
			t := jwt.New()
			_ = t.Set(jwt.SubjectKey, servicesID)
			_ = t.Set(jwt.IssuerKey, teamID)
			_ = t.Set(jwt.IssuedAtKey, now.Unix())
			_ = t.Set(jwt.AudienceKey, "https://appleid.apple.com")
			_ = t.Set(jwt.ExpirationKey, now.Add(time.Hour*24*180).Unix())

			headers := jws.NewHeaders()
			_ = headers.Set(jws.KeyIDKey, keyID)

			signed, err := jwt.Sign(t, jwt.WithKey(jwa.ES256, key, jws.WithProtectedHeaders(headers)))
			fmt.Println(string(signed))
		},
	}

	cmd.Flags().StringVarP(&privateKey, "private_key", "p", "", "Path to the private key file")
	cmd.Flags().StringVarP(&teamID, "team_id", "t", "", "Apple team ID")
	cmd.Flags().StringVarP(&servicesID, "services_id", "s", "", "Apple services ID")
	cmd.Flags().StringVarP(&keyID, "key_id", "k", "", "Apple key ID")

	_ = cmd.MarkFlagRequired("private_key")
	_ = cmd.MarkFlagRequired("team_id")
	_ = cmd.MarkFlagRequired("services_id")
	_ = cmd.MarkFlagRequired("key_id")

	return cmd
}

func RegisterCommands(parent *cobra.Command) {
	parent.AddCommand(NewSignInWithAppleCommand())
}


================================================
FILE: backend/cmd/user/export.go
================================================
package user

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/persistence"
)

func NewExportCommand() *cobra.Command {
	var (
		configFile string
		outputFile string
	)

	cmd := &cobra.Command{
		Use:   "export",
		Short: "Export users from database into a Json file",
		Long:  ``,
		Run: func(cmd *cobra.Command, args []string) {
			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}
			persister, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}
			err = export(persister, outputFile)
			if err != nil {
				log.Fatal(err)
			}
			log.Println(fmt.Sprintf("Successfully exported users to %s", outputFile))
		},
	}

	cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")
	cmd.Flags().StringVarP(&outputFile, "outputFile", "o", "", "The path of the output file.")
	err := cmd.MarkFlagRequired("outputFile")
	if err != nil {
		log.Println(err)
	}
	return cmd
}

func export(persister persistence.Persister, outFile string) error {
	var entries []ImportOrExportEntry
	users, err := persister.GetUserPersister().All()
	if err != nil {
		return fmt.Errorf("failed to get list of users: %w", err)
	}
	for _, user := range users {
		var emails []ImportOrExportEmail
		for _, email := range user.Emails {
			emails = append(emails, ImportOrExportEmail{
				Address:    email.Address,
				IsPrimary:  email.IsPrimary(),
				IsVerified: email.Verified,
			})
		}
		entry := ImportOrExportEntry{
			UserID:    user.ID.String(),
			Emails:    emails,
			CreatedAt: &user.CreatedAt,
			UpdatedAt: &user.UpdatedAt,
		}
		entries = append(entries, entry)
	}
	bytes, err := json.Marshal(entries)
	if err != nil {
		return err
	}
	err = os.WriteFile(outFile, bytes, 0600)
	if err != nil {
		return err
	}
	return nil
}


================================================
FILE: backend/cmd/user/format.go
================================================
package user

import (
	"errors"
	"fmt"
	"github.com/go-playground/validator/v10"
	"github.com/gofrs/uuid"
	"github.com/invopop/jsonschema"
	"time"
)

// ImportOrExportEmail The import/export format for a user's email
type ImportOrExportEmail struct {
	// Address Valid email address
	Address string `json:"address" yaml:"address" jsonschema:"format=email" validate:"email"`
	// IsPrimary indicates if this is the primary email of the users. In the Emails array there has to be exactly one primary email.
	IsPrimary bool `json:"is_primary" yaml:"is_primary"`
	// IsVerified indicates if the email address was previously verified.
	IsVerified bool `json:"is_verified" yaml:"is_verified"`
}

func (ImportOrExportEmail) JSONSchemaExtend(schema *jsonschema.Schema) {
	schema.Title = "ImportEmail"
}

// Emails Array of email addresses
type Emails []ImportOrExportEmail

type ImportWebauthnCredential struct {
	// ID of the WebAuthn credential.
	ID string `json:"id" yaml:"id" validate:"required"`
	// Optional Name of the WebAuthn credential.
	Name *string `json:"name" yaml:"name" validate:"omitempty"`
	// The PublicKey of the credential.
	PublicKey string `json:"public_key" yaml:"public_key" validate:"required"`
	// The AttestationType the credential was created with.
	AttestationType string `json:"attestation_type" yaml:"attestation_type" validate:"required"`
	// Optional AAGUID of the authenticator on which the credential was created on.
	AAGUID uuid.UUID `json:"aaguid" yaml:"aaguid" validate:"omitempty,uuid4"`
	// Optional SignCount of the WebAuthn credential.
	SignCount int `json:"sign_count" yaml:"sign_count"`
	// LastUsedAt optional timestamp when the WebAuthn credential was last used.
	LastUsedAt *time.Time `json:"last_used_at" yaml:"last_used_at" validate:"omitempty"`
	// CreatedAt optional timestamp of the WebAuthn credentials' creation. Will be set to the import date if not provided.
	CreatedAt *time.Time `json:"created_at" yaml:"created_at" validate:"omitempty"`
	// UpdatedAt optional timestamp of the last update to the WebAuthn credential. Will be set to the import date if not provided.
	UpdatedAt *time.Time `json:"updated_at" yaml:"updated_at" validate:"omitempty"`
	// Optional list of supported Transports by the authenticator.
	Transports []string `json:"transports" yaml:"transports" validate:"omitempty,unique"`
	// BackupEligible flag indicates if the WebAuthn credential can be backed up (e.g. in Apple KeyChain, ...). If the information is not available set it to false.
	BackupEligible bool `json:"backup_eligible" yaml:"backup_eligible"`
	// BackupState flag indicates if the WebAuthn credential is backed up (e.g. in Apple KeyChain, ...). If the information is not available set it to false.
	BackupState bool `json:"backup_state" yaml:"backup_state"`
	// MFAOnly flag indicates if the WebAuthn credential can only be used in combination with another login factor (e.g. password, ...).
	MFAOnly bool `json:"mfa_only" yaml:"mfa_only"`
	// UserHandle optional user id which was used to create the credential with.
	// Populate only when user id was not an uuid v4 and the WebAuthn credential is not an MFAOnly credential.
	UserHandle *string `json:"user_handle" yaml:"user_handle" validate:"omitempty,excluded_if=MFAOnly true"`
}

type ImportWebauthnCredentials []ImportWebauthnCredential

type ImportPasswordCredential struct {
	// Password hash of the password in bcrypt format.
	Password string `json:"password" yaml:"password" validate:"required,startswith=$2a$"`
	// CreatedAt optional timestamp when the password was created. Will be set to the import date if not provided.
	CreatedAt *time.Time `json:"created_at,omitempty" yaml:"created_at" validate:"omitempty"`
	// UpdatedAt optional timestamp of the last update to the password. Will be set to the import date if not provided.
	UpdatedAt *time.Time `json:"updated_at,omitempty" yaml:"updated_at" validate:"omitempty"`
}

type ImportOTPSecret struct {
	// Secret of the TOTP credential. TOTP credential must be generated for a period of 30 seconds and SHA1 hash algorithm.
	Secret string `json:"secret" yaml:"secret" validate:"required"`
	// CreatedAt optional timestamp when the otp secret was created. Will be set to the import date if not provided.
	CreatedAt *time.Time `json:"created_at,omitempty" yaml:"created_at" validate:"omitempty"`
	// UpdatedAt optional timestamp of the last update to the otp secret. Will be set to the import date if not provided.
	UpdatedAt *time.Time `json:"updated_at,omitempty" yaml:"updated_at" validate:"omitempty"`
}

// ImportOrExportEntry represents a user to be imported/export to the Hanko database
type ImportOrExportEntry struct {
	// UserID optional uuid.v4. If not provided a new one will be generated for the user
	UserID string `json:"user_id,omitempty" yaml:"user_id" validate:"omitempty,uuid"`
	// Emails optional list of emails
	Emails Emails `json:"emails" yaml:"emails" jsonschema:"type=array,minItems=1" validate:"required_if=Username 0,unique=Address,dive"`
	// Username optional username of the user
	Username *string `json:"username,omitempty" yaml:"username" validate:"required_if=Emails 0,omitempty,gte=1"`
	// WebauthnCredentials optional list of WebAuthn credentials of a user. Includes passkeys and MFA credentials.
	WebauthnCredentials ImportWebauthnCredentials `json:"webauthn_credentials,omitempty" yaml:"webauthn_credentials" validate:"omitempty,unique=ID,dive"`
	// Password optional password.
	Password *ImportPasswordCredential `json:"password" yaml:"password" validate:"omitempty"`
	// OTPSecret optional TOTP secret for MFA.
	OTPSecret *ImportOTPSecret `json:"otp_secret" yaml:"otp_secret" validate:"omitempty"`
	// CreatedAt optional timestamp of the users' creation. Will be set to the import date if not provided.
	CreatedAt *time.Time `json:"created_at,omitempty" yaml:"created_at" validate:"omitempty"`
	// UpdatedAt optional timestamp of the last update to the user. Will be set to the import date if not provided.
	UpdatedAt *time.Time `json:"updated_at,omitempty" yaml:"updated_at" validate:"omitempty"`
}

func (ImportOrExportEntry) JSONSchemaExtend(schema *jsonschema.Schema) {
	schema.Title = "ImportEntry"
}

// ImportOrExportList a list of ImportEntries
type ImportOrExportList []ImportOrExportEntry

func (ImportOrExportList) JSONSchemaExtend(schema *jsonschema.Schema) {
	date := time.Date(2024, 8, 17, 12, 5, 15, 651387237, time.UTC)
	username := "example"
	schema.Examples = []any{
		[]ImportOrExportEntry{
			{
				UserID: "a9ae6bc8-d829-43de-b672-f50230833877",
				Emails: Emails{
					{"test@example.com", true, true},
					{"test+1@example.com", false, false},
				},
				CreatedAt: &date,
				UpdatedAt: &date,
			},
			{
				UserID: "2f0649cf-c71e-48a5-92c3-210addb80281",
				Emails: Emails{
					{"test2@example.com", true, true},
					{"test2+1@example.com", false, false},
				},
				CreatedAt: &date,
				UpdatedAt: &date,
			},
		},
		[]ImportOrExportEntry{
			{
				Username: &username,
				Password: &ImportPasswordCredential{
					Password:  "$2a$12$mFbud0mLsD/q.WG7/9pNQemlAHs3H4o8zAv44gsUF1v1awsdqTh7.",
					CreatedAt: &date,
					UpdatedAt: &date,
				},
			},
		},
	}
}

func (entry *ImportOrExportEntry) validate(v *validator.Validate) error {
	err := v.Struct(entry)
	if err != nil {
		return err
	}
	primaryEmailAddresses := 0
	for _, email := range entry.Emails {
		if email.IsPrimary {
			primaryEmailAddresses++
		}
	}

	if len(entry.Emails) > 0 && primaryEmailAddresses != 1 {
		return errors.New(fmt.Sprintf("Need exactly one primary email, got %v", primaryEmailAddresses))
	}

	return nil
}


================================================
FILE: backend/cmd/user/format_test.go
================================================
package user

import (
	"fmt"
	"github.com/go-playground/validator/v10"
	"github.com/gofrs/uuid"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

const validUUID = "62418053-a2cd-47a8-9b61-4426380d263a"
const invalidUUID = "notvalid"

func TestImportEntry_validate(t *testing.T) {
	v := validator.New()
	validUsername := "example"
	emptyUsername := ""
	type fields struct {
		UserID              string
		Emails              Emails
		Username            *string
		WebauthnCredentials ImportWebauthnCredentials
		Password            *ImportPasswordCredential
		OTPSecret           *ImportOTPSecret
		CreatedAt           *time.Time
		UpdatedAt           *time.Time
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr assert.ErrorAssertionFunc
	}{
		{
			name: "User with one primary email must validate",
			fields: fields{
				UserID: "",
				Emails: Emails{
					ImportOrExportEmail{
						Address:    "primary@hanko.io",
						IsPrimary:  true,
						IsVerified: false,
					},
				},
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			wantErr: assert.NoError,
		},
		{
			name: "User with no email but username must validate",
			fields: fields{
				Username: &validUsername,
			},
			wantErr: assert.NoError,
		},
		{
			name: "UserID with valid uuid must validate",
			fields: fields{
				UserID: validUUID,
				Emails: Emails{
					ImportOrExportEmail{
						Address:    "primary@hanko.io",
						IsPrimary:  true,
						IsVerified: false,
					},
				},
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			wantErr: assert.NoError,
		},
		{
			name: "UserID with invalid uuid must not validate",
			fields: fields{
				UserID: invalidUUID,
				Emails: Emails{
					ImportOrExportEmail{
						Address:    "primary@hanko.io",
						IsPrimary:  true,
						IsVerified: false,
					},
				},
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			wantErr: assert.Error,
		},
		{
			name: "User with no email and username must not validate",
			fields: fields{
				UserID:    "",
				Emails:    nil,
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			wantErr: assert.Error,
		},
		{
			name: "User with an empty username must not validate",
			fields: fields{
				Emails: Emails{
					ImportOrExportEmail{
						Address:    "primary@hanko.io",
						IsPrimary:  true,
						IsVerified: false,
					},
				},
				Username: &emptyUsername,
			},
			wantErr: assert.Error,
		},
		{
			name: "User with no primary email must not validate",
			fields: fields{
				UserID: "",
				Emails: Emails{
					ImportOrExportEmail{
						Address:    "primary@hanko.io",
						IsPrimary:  false,
						IsVerified: false,
					},
				},
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			wantErr: assert.Error,
		},
		{
			name: "More than one Primary must not validate",
			fields: fields{
				UserID: "",
				Emails: Emails{
					ImportOrExportEmail{
						Address:    "primary@hanko.io",
						IsPrimary:  true,
						IsVerified: false,
					},
					ImportOrExportEmail{
						Address:    "primary2@hanko.io",
						IsPrimary:  true,
						IsVerified: false,
					},
				},
				CreatedAt: nil,
				UpdatedAt: nil,
			},
			wantErr: assert.Error,
		},
		{
			name: "Valid webauthn credential must validate",
			fields: fields{
				Username: &validUsername,
				WebauthnCredentials: ImportWebauthnCredentials{
					webauthnCredential,
				},
			},
			wantErr: assert.NoError,
		},
		{
			name: "Webauthn credential without id must not validate",
			fields: fields{
				Username: &validUsername,
				WebauthnCredentials: ImportWebauthnCredentials{
					webauthnCredentialWithEmptyID,
				},
			},
			wantErr: assert.Error,
		},
		{
			name: "Webauthn credential without public key must not validate",
			fields: fields{
				Username: &validUsername,
				WebauthnCredentials: ImportWebauthnCredentials{
					webauthnCredentialWithEmptyPublicKey,
				},
			},
			wantErr: assert.Error,
		},
		{
			name: "Webauthn credential without attestation type must not validate",
			fields: fields{
				Username: &validUsername,
				WebauthnCredentials: ImportWebauthnCredentials{
					webauthnCredentialWithEmptyAttestationType,
				},
			},
			wantErr: assert.Error,
		},
		{
			name: "User with password must validate",
			fields: fields{
				Username: &validUsername,
				Password: &ImportPasswordCredential{
					Password: "$2a$12$mFbud0mLsD/q.WG7/9pNQemlAHs3H4o8zAv44gsUF1v1awsdqTh7.",
				},
			},
			wantErr: assert.NoError,
		},
		{
			name: "User with empty password string must not validate",
			fields: fields{
				Username: &validUsername,
				Password: &ImportPasswordCredential{
					Password: "",
				},
			},
			wantErr: assert.Error,
		},
		{
			name: "Wrong formatted password must not validate",
			fields: fields{
				Username: &validUsername,
				Password: &ImportPasswordCredential{
					Password: "$12$mFbud0mLsD/q.WG7/9pNQemlAHs3H4o8zAv44gsUF1v1awsdqTh7.",
				},
			},
			wantErr: assert.Error,
		},
		{
			name: "User with OTPSecret must validate",
			fields: fields{
				Username: &validUsername,
				OTPSecret: &ImportOTPSecret{
					Secret: "MYOTPSECRET",
				},
			},
			wantErr: assert.NoError,
		},
		{
			name: "User with empty OTPSecret string must not validate",
			fields: fields{
				Username: &validUsername,
				OTPSecret: &ImportOTPSecret{
					Secret: "",
				},
			},
			wantErr: assert.Error,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			entry := &ImportOrExportEntry{
				UserID:              tt.fields.UserID,
				Emails:              tt.fields.Emails,
				CreatedAt:           tt.fields.CreatedAt,
				UpdatedAt:           tt.fields.UpdatedAt,
				Username:            tt.fields.Username,
				WebauthnCredentials: tt.fields.WebauthnCredentials,
				Password:            tt.fields.Password,
				OTPSecret:           tt.fields.OTPSecret,
			}
			tt.wantErr(t, entry.validate(v), fmt.Sprintf("validate()"))
		})
	}
}

var webauthnCredential = ImportWebauthnCredential{
	ID:              "randomID",
	Name:            nil,
	PublicKey:       "randomPublicKey",
	AttestationType: "none",
	AAGUID:          uuid.Nil,
	SignCount:       0,
	LastUsedAt:      nil,
	CreatedAt:       nil,
	UpdatedAt:       nil,
	Transports:      nil,
	BackupEligible:  false,
	BackupState:     false,
	MFAOnly:         false,
}

var webauthnCredentialWithEmptyID = ImportWebauthnCredential{
	ID:              "",
	Name:            nil,
	PublicKey:       "randomPublicKey",
	AttestationType: "none",
	AAGUID:          uuid.Nil,
	SignCount:       0,
	LastUsedAt:      nil,
	CreatedAt:       nil,
	UpdatedAt:       nil,
	Transports:      nil,
	BackupEligible:  false,
	BackupState:     false,
	MFAOnly:         false,
}

var webauthnCredentialWithEmptyPublicKey = ImportWebauthnCredential{
	ID:              "randomID",
	Name:            nil,
	PublicKey:       "",
	AttestationType: "none",
	AAGUID:          uuid.Nil,
	SignCount:       0,
	LastUsedAt:      nil,
	CreatedAt:       nil,
	UpdatedAt:       nil,
	Transports:      nil,
	BackupEligible:  false,
	BackupState:     false,
	MFAOnly:         false,
}

var webauthnCredentialWithEmptyAttestationType = ImportWebauthnCredential{
	ID:              "randomID",
	Name:            nil,
	PublicKey:       "randomPublicKey",
	AttestationType: "",
	AAGUID:          uuid.Nil,
	SignCount:       0,
	LastUsedAt:      nil,
	CreatedAt:       nil,
	UpdatedAt:       nil,
	Transports:      nil,
	BackupEligible:  false,
	BackupState:     false,
	MFAOnly:         false,
}


================================================
FILE: backend/cmd/user/generate.go
================================================
package user

import (
	"encoding/json"
	"log"
	"os"
	"time"

	"github.com/brianvoe/gofakeit/v6"
	"github.com/gofrs/uuid"
	"github.com/spf13/cobra"
)

var outputFile string
var count int

func NewGenerateCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "generate",
		Short: "Generate mock users and write them to a file.",
		Long:  ``,
		Run: func(cmd *cobra.Command, args []string) {
			err := generate()
			if err != nil {
				log.Println(err)
			}
		},
	}

	cmd.Flags().StringVarP(&outputFile, "outputFile", "o", "", "The path of the output file.")
	err := cmd.MarkFlagRequired("outputFile")
	if err != nil {
		log.Println(err)
	}
	cmd.Flags().IntVarP(&count, "count", "c", 10, "Gives the number of users that should be generated.")
	return cmd
}

func generate() error {
	var entries []ImportOrExportEntry
	for i := 0; i < count; i++ {
		now := time.Now().UTC()
		id, _ := uuid.NewV4()
		emails := []ImportOrExportEmail{
			{
				Address:    gofakeit.Email(),
				IsPrimary:  true,
				IsVerified: true,
			},
		}
		entry := ImportOrExportEntry{
			UserID:    id.String(),
			Emails:    emails,
			CreatedAt: &now,
			UpdatedAt: &now,
		}
		entries = append(entries, entry)
	}
	bytes, err := json.Marshal(entries)
	if err != nil {
		return err
	}
	err = os.WriteFile(outputFile, bytes, 0600)
	if err != nil {
		return err
	}

	return nil
}


================================================
FILE: backend/cmd/user/import.go
================================================
package user

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-playground/validator/v10"
	"github.com/teamhanko/hanko/backend/v2/dto"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gobuffalo/pop/v6"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/config"
	"github.com/teamhanko/hanko/backend/v2/persistence"
)

func NewImportCommand() *cobra.Command {
	var (
		configFile string
		inputFile  string
		inputUrl   string
	)

	cmd := &cobra.Command{
		Use:   "import",
		Short: "Import users into database from a Json file",
		Long:  ``,
		PreRunE: func(cmd *cobra.Command, args []string) error {
			fileFlagSet := cmd.Flags().Changed("inputFile")
			urlFlagSet := cmd.Flags().Changed("inputUrl")
			if !fileFlagSet && !urlFlagSet {
				return errors.New("either flag \"inputFile\" or \"inputUrl\" must be set")
			}
			return nil
		},
		Run: func(cmd *cobra.Command, args []string) {
			//Load cfg
			cfg, err := config.Load(&configFile)
			if err != nil {
				log.Fatal(err)
			}

			var reader io.ReadCloser
			fileAvailable := cmd.Flags().Changed("inputFile")
			urlAvailable := cmd.Flags().Changed("inputUrl")

			if fileAvailable {
				//Load File
				// we explicitly want user input here, hence  #nosec G304
				reader, err = os.Open(inputFile)
				if err != nil {
					log.Fatal(err)
				}
			} else if urlAvailable {
				// Load file from url
				response, err := http.Get(inputUrl)
				if err != nil {
					log.Fatal(err)
				}

				if response.StatusCode < 200 || response.StatusCode > 299 {
					log.Fatal(fmt.Errorf("failed to get file from url: %s", response.Status))
				}

				reader = response.Body
			}

			defer func() {
				if reader != nil {
					if err := reader.Close(); err != nil {
						log.Printf("Error closing file: %s\n", err)
					}
				}
			}()

			users, err := loadAndValidate(reader)
			if err != nil {
				log.Fatal(err)
			}
			//Import Users
			persister, err := persistence.New(cfg.Database)
			if err != nil {
				log.Fatal(err)
			}
			err = addToDatabase(users, persister)
			if err != nil {
				log.Fatal(err)
			}
			log.Println(fmt.Sprintf("Successfully imported %v users.", len(users)))
		},
	}

	cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")
	cmd.Flags().StringVarP(&inputFile, "inputFile", "i", "", "The json file where the users should be imported from.")
	cmd.Flags().StringVarP(&inputUrl, "inputUrl", "u", "", "The url to a json file where the users should be imported from.")
	cmd.MarkFlagsMutuallyExclusive("inputFile", "inputUrl")
	return cmd
}

// loadAndValidate reads json from an io.Reader so we read every entry separate and validate it. We go through the whole
// array to print out every validation error in the input data.
func loadAndValidate(input io.Reader) ([]ImportOrExportEntry, error) {
	dec := json.NewDecoder(input)

	// read the open bracket
	_, err := dec.Token()
	if err != nil {
		return nil, err
	}

	users := []ImportOrExportEntry{}
	v := validator.New()

	numErrors := 0
	index := 0
	// while the array contains values
	for dec.More() {
		index = index + 1
		var userEntry ImportOrExportEntry
		// decode one ImportEntry
		err := dec.Decode(&userEntry)
		if err != nil {
			errorMsg := fmt.Sprintf("Error at entry %v : %v", index, err.Error())
			log.Println(errorMsg)
			return nil, err
		}

		if err := userEntry.validate(v); err != nil {
			vErrs := dto.TransformValidationErrors(err)
			errorMsg := fmt.Sprintf("Error at entry %v : %v", index, strings.Join(vErrs, " and "))
			log.Println(errorMsg)
			numErrors++
			continue
		}
		users = append(users, userEntry)
	}

	// read closing bracket
	_, err = dec.Token()
	if err != nil {
		return nil, err
	}
	if numErrors > 0 {
		errMsg := fmt.Sprintf("Found %v errors.", numErrors)
		return nil, errors.New(errMsg)
	}

	return users, nil
}

// commits the list of ImportEntries to the database. Wrapped in a transaction so if something fails no new users are added.
func addToDatabase(entries []ImportOrExportEntry, persister persistence.Persister) error {
	tx := persister.GetConnection()
	err := tx.Transaction(func(tx *pop.Connection) error {
		importer := Importer{
			persister:       persister,
			tx:              tx,
			importTimestamp: time.Now().UTC(),
		}
		for i, v := range entries {
			userModel, err := importer.createUser(v)
			if err != nil {
				return fmt.Errorf("failed to create user entry nr. %d: %w", i, err)
			}

			for _, e := range v.Emails {
				emailModel, err := importer.createEmailAddress(userModel.ID, e)
				if err != nil {
					return fmt.Errorf("failed to create email address \"%s\" for user entry nr. %d: %w", e.Address, i, err)
				}
				if e.IsPrimary {
					err = importer.createPrimaryEmailAddress(userModel.ID, emailModel.ID)
					if err != nil {
						return fmt.Errorf("failed to set email \"%s\" as primary for user entry nr. %d: %w", e.Address, i, err)
					}
				}
			}

			if v.Username != nil {
				err = importer.createUsername(userModel.ID, *v.Username)
				if err != nil {
					return fmt.Errorf("failed to create username \"%v\" for user entry nr. %d: %w", v.Username, i, err)
				}
			}

			for _, credential := range v.WebauthnCredentials {
				err = importer.createWebauthnCredential(userModel.ID, credential)
				if err != nil {
					return fmt.Errorf("failed to create webauthn credential \"%s\" for user entry nr. %d: %w", credential.ID, i, err)
				}
			}

			if v.Password != nil {
				err = importer.createPasswordCredential(userModel.ID, *v.Password)
				if err != nil {
					return fmt.Errorf("failed to create password for user entry nr. %d: %w", i, err)
				}
			}

			if v.OTPSecret != nil {
				err = importer.createOTPSecret(userModel.ID, *v.OTPSecret)
				if err != nil {
					return fmt.Errorf("failed to create otp secret for user entry nr. %d: %w", i, err)
				}
			}
		}
		return nil
	})

	return err
}


================================================
FILE: backend/cmd/user/import_test.go
================================================
package user

import (
	"fmt"
	"io"
	"log"
	"strings"
	"testing"
	"time"

	"github.com/gofrs/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"github.com/teamhanko/hanko/backend/v2/test"
)

const validUUID2 = "799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3"

func TestImportSuite(t *testing.T) {
	t.Parallel()
	suite.Run(t, new(importSuite))
}

type importSuite struct {
	test.Suite
}

func (s *importSuite) Test_loadAndValidate() {
	type args struct {
		input io.Reader
	}
	standardTime, _ := time.Parse(time.RFC3339, "2023-06-07T13:42:49.369489Z")
	tests := []struct {
		name    string
		args    args
		want    []ImportOrExportEntry
		wantErr assert.ErrorAssertionFunc
	}{
		{
			name: "empty array -> empty result",
			args: args{
				input: strings.NewReader("[]"),
			},
			wantErr: assert.NoError,
			want:    []ImportOrExportEntry{},
		},
		{
			name: "empty file -> nil result",
			args: args{
				input: strings.NewReader(""),
			},
			wantErr: assert.Error,
			want:    nil,
		},
		{
			name: "one user file",
			args: args{
				input: strings.NewReader("[{\"user_id\":\"799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3\",\"emails\":[{\"address\":\"koreyrath@wolff.name\",\"is_primary\":true,\"is_verified\":true}],\"created_at\":\"2023-06-07T13:42:49.369489Z\",\"updated_at\":\"2023-06-07T13:42:49.369489Z\"}]\n"),
			},
			wantErr: assert.NoError,
			want: []ImportOrExportEntry{
				{
					UserID: validUUID2,
					Emails: Emails{
						ImportOrExportEmail{
							Address:    "koreyrath@wolff.name",
							IsPrimary:  true,
							IsVerified: true,
						},
					},
					CreatedAt: &standardTime,
					UpdatedAt: &standardTime,
				},
			},
		},
		{
			name: "corrupted json input",
			args: args{
				input: strings.NewReader("[{user_id:\"799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3\",}]\n"),
			},
			wantErr: assert.Error,
			want:    nil,
		},
		{
			name: "several validation errors",
			args: args{
				input: strings.NewReader("[{\"user_id\":\"799e95f0-4cc7-4bd7-9f1-5fdc4fa26ea3\",\"emails\":[{\"address\":\"koreyrath@wolff.name\",\"is_primary\":true,\"is_verified\":true}],\"created_at\":\"2023-06-07T13:42:49.369489Z\",\"updated_at\":\"2023-06-07T13:42:49.369489Z\"},{\"user_id\":\"799e95f0-4cc7-4bd7-9f1-5fdc4fa26ea3\",\"emails\":[{\"address\":\"koreyrath@wolff.name\",\"is_primary\":false,\"is_verified\":true}],\"created_at\":\"2023-06-07T13:42:49.369489Z\",\"updated_at\":\"2023-06-07T13:42:49.369489Z\"}]\n"),
			},
			wantErr: assert.Error,
			want:    nil,
		},
	}
	for _, tt := range tests {
		s.Run(tt.name, func() {
			got, err := loadAndValidate(tt.args.input)
			if !tt.wantErr(s.T(), err, fmt.Sprintf("loadAndValidate(%v)", tt.args.input)) {
				return
			}
			assert.Equalf(s.T(), tt.want, got, "loadAndValidate(%v)", tt.args.input)
		})
	}
}

func (s *importSuite) Test_addToDatabase() {
	if testing.Short() {
		s.T().Skip("skipping test in short mode.")
	}

	type args struct {
		entries   []ImportOrExportEntry
		persister persistence.Persister
	}
	tests := []struct {
		name         string
		args         args
		wantErr      assert.ErrorAssertionFunc
		wantNumUsers int
	}{
		{
			name: "Positive",
			args: args{
				entries: []ImportOrExportEntry{
					{
						UserID: "",
						Emails: Emails{
							ImportOrExportEmail{
								Address:    "primary@hanko.io",
								IsPrimary:  true,
								IsVerified: false,
							},
						},
						CreatedAt: nil,
						UpdatedAt: nil,
					},
				},
				persister: s.Storage,
			},
			wantErr:      assert.NoError,
			wantNumUsers: 1,
		},
		{
			name: "Double uuid",
			args: args{
				entries: []ImportOrExportEntry{
					{
						UserID: validUUID,
						Emails: Emails{
							ImportOrExportEmail{
								Address:    "primary1@hanko.io",
								IsPrimary:  true,
								IsVerified: false,
							},
						},
						CreatedAt: nil,
						UpdatedAt: nil,
					},
					{
						UserID: validUUID,
						Emails: Emails{
							ImportOrExportEmail{
								Address:    "primary2@hanko.io",
								IsPrimary:  true,
								IsVerified: false,
							},
						},
						CreatedAt: nil,
						UpdatedAt: nil,
					},
				},
				persister: s.Storage,
			},
			wantErr:      assert.Error,
			wantNumUsers: 0,
		},
		{
			name: "Double primary email",
			args: args{
				entries: []ImportOrExportEntry{
					{
						UserID: validUUID,
						Emails: Emails{
							ImportOrExportEmail{
								Address:    "primary@hanko.io",
								IsPrimary:  true,
								IsVerified: false,
							},
						},
						CreatedAt: nil,
						UpdatedAt: nil,
					},
					{
						UserID: validUUID,
						Emails: Emails{
							ImportOrExportEmail{
								Address:    "primary@hanko.io",
								IsPrimary:  true,
								IsVerified: false,
							},
						},
						CreatedAt: nil,
						UpdatedAt: nil,
					},
				},
				persister: s.Storage,
			},
			wantErr:      assert.Error,
			wantNumUsers: 0,
		},
	}
	for _, tt := range tests {
		s.T().Run(tt.name, func(t *testing.T) {

			s.SetupTest()
			tt.wantErr(t, addToDatabase(tt.args.entries, tt.args.persister), fmt.Sprintf("addToDatabase(%v, %v)", tt.args.entries, tt.args.persister))
			users, err := tt.args.persister.GetUserPersister().List(0, 100, []uuid.UUID{}, "", "", "")
			log.Println(users)
			s.NoError(err)
			s.Equal(tt.wantNumUsers, len(users))

			s.TearDownTest()
		})
	}
}


================================================
FILE: backend/cmd/user/importer.go
================================================
package user

import (
	"github.com/gobuffalo/pop/v6"
	"github.com/gofrs/uuid"
	"github.com/teamhanko/hanko/backend/v2/persistence"
	"github.com/teamhanko/hanko/backend/v2/persistence/models"
	"strings"
	"time"
)

type Importer struct {
	persister       persistence.Persister
	tx              *pop.Connection
	importTimestamp time.Time
}

func (i *Importer) createUser(newUser ImportOrExportEntry) (*models.User, error) {
	userID, err := uuid.NewV4()
	if err != nil {
		return nil, err
	}
	userModel := models.User{
		ID:        userID,
		CreatedAt: i.importTimestamp,
		UpdatedAt: i.importTimestamp,
	}

	if newUser.UserID != "" {
		userModel.ID = uuid.FromStringOrNil(newUser.UserID)
	}

	if newUser.CreatedAt != nil {
		userModel.CreatedAt = newUser.CreatedAt.UTC()
	}

	if newUser.UpdatedAt != nil {
		userModel.UpdatedAt = newUser.UpdatedAt.UTC()
	}

	err = i.persister.GetUserPersisterWithConnection(i.tx).Create(userModel)
	if err != nil {
		return nil, err
	}

	return &userModel, nil
}

func (i *Importer) createEmailAddress(userID uuid.UUID, newEmail ImportOrExportEmail) (*models.Email, error) {
	emailID, err := uuid.NewV4()
	if err != nil {
		return nil, err
	}

	emailModel := models.Email{
		ID:        emailID,
		UserID:    &userID,
		Address:   strings.ToLower(newEmail.Address),
		Verified:  newEmail.IsVerified,
		CreatedAt: i.importTimestamp,
		UpdatedAt: i.importTimestamp,
	}

	err = i.persister.GetEmailPersisterWithConnection(i.tx).Create(emailModel)
	if err != nil {
		return nil, err
	}

	return &emailModel, nil
}

func (i *Importer) createPrimaryEmailAddress(userID uuid.UUID, emailID uuid.UUID) error {
	entryID, err := uuid.NewV4()
	if err != nil {
		return err
	}

	primaryEmailModel := models.PrimaryEmail{
		ID:        entryID,
		EmailID:   emailID,
		UserID:    userID,
		CreatedAt: i.importTimestamp,
		UpdatedAt: i.importTimestamp,
	}

	err = i.persister.GetPrimaryEmailPersisterWithConnection(i.tx).Create(primaryEmailModel)
	return err
}

func (i *Importer) createUsername(userID uuid.UUID, username string) error {
	entryID, err := uuid.NewV4()
	if err != nil {
		return err
	}

	usernameModel := models.Username{
		ID:        entryID,
		UserId:    userID,
		Username:  username,
		CreatedAt: i.importTimestamp,
		UpdatedAt: i.importTimestamp,
	}

	err = i.persister.GetUsernamePersisterWithConnection(i.tx).Create(usernameModel)
	return err
}

func (i *Importer) createWebauthnCredential(userID uuid.UUID, webauthnCredential ImportWebauthnCredential) error {
	createdAt := i.importTimestamp
	updatedAt := i.importTimestamp
	if webauthnCredential.CreatedAt != nil {
		createdAt = webauthnCredential.CreatedAt.UTC()
	}

	if webauthnCredential.UpdatedAt != nil {
		updatedAt = webauthnCredential.UpdatedAt.UTC()
	}

	var transports models.Transports = nil
	for _, transport := range webauthnCredential.Transports {
		transportID, err := uuid.NewV4()
		if err != nil {
			return err
		}
		transports = append(transports, models.WebauthnCredentialTransport{
			ID:                   transportID,
			Name:                 transport,
			WebauthnCredentialID: webauthnCredential.ID,
		})
	}

	webauthnCredentialModel := models.WebauthnCredential{
		ID:              webauthnCredential.ID,
		Name:            webauthnCredential.Name,
		UserId:          userID,
		PublicKey:       webauthnCredential.PublicKey,
		AttestationType: webauthnCredential.AttestationType,
		AAGUID:          webauthnCredential.AAGUID,
		SignCount:       webauthnCredential.SignCount,
		LastUsedAt:      webauthnCredential.LastUsedAt,
		CreatedAt:       createdAt,
		UpdatedAt:       updatedAt,
		Transports:      transports,
		BackupEligible:  webauthnCredential.BackupEligible,
		BackupState:     webauthnCredential.BackupState,
		MFAOnly:         webauthnCredential.MFAOnly,
	}

	if webauthnCredential.UserHandle != nil {
		existingUserHandle, err := i.persister.GetWebauthnCredentialUserHandlePersisterWithConnection(i.tx).GetByHandle(*webauthnCredential.UserHandle)
		if err != nil {
			return err
		}

		if existingUserHandle != nil {
			webauthnCredentialModel.UserHandleID = &existingUserHandle.ID
		} else {
			userHandleID, err := uuid.NewV4()
			if err != nil {
				return err
			}

			userHandle := models.WebauthnCredentialUserHandle{
				ID:        userHandleID,
				UserID:    userID,
				Handle:    *webauthnCredential.UserHandle,
				CreatedAt: i.importTimestamp,
				UpdatedAt: i.importTimestamp,
			}
			webauthnCredentialModel.UserHandle = &userHandle
			webauthnCredentialModel.UserHandleID = &userHandleID
		}
	}

	err := i.persister.GetWebauthnCredentialPersisterWithConnection(i.tx).Create(webauthnCredentialModel)
	return err
}

func (i *Importer) createPasswordCredential(userID uuid.UUID, passwordCredential ImportPasswordCredential) error {
	passwordID, err := uuid.NewV4()
	if err != nil {
		return err
	}

	createdAt := i.importTimestamp
	updatedAt := i.importTimestamp
	if passwordCredential.CreatedAt != nil {
		createdAt = passwordCredential.CreatedAt.UTC()
	}

	if passwordCredential.UpdatedAt != nil {
		updatedAt = passwordCredential.UpdatedAt.UTC()
	}

	passwordModel := models.PasswordCredential{
		ID:        passwordID,
		UserId:    userID,
		Password:  passwordCredential.Password,
		CreatedAt: createdAt,
		UpdatedAt: updatedAt,
	}

	err = i.persister.GetPasswordCredentialPersisterWithConnection(i.tx).Create(passwordModel)
	return err
}

func (i *Importer) createOTPSecret(userID uuid.UUID, otpSecret ImportOTPSecret) error {
	otpSecretID, err := uuid.NewV4()
	if err != nil {
		return err
	}

	createdAt := i.importTimestamp
	updatedAt := i.importTimestamp
	if otpSecret.CreatedAt != nil {
		createdAt = otpSecret.CreatedAt.UTC()
	}

	if otpSecret.UpdatedAt != nil {
		updatedAt = otpSecret.UpdatedAt.UTC()
	}

	otpSecretModel := models.OTPSecret{
		ID:        otpSecretID,
		UserID:    userID,
		Secret:    otpSecret.Secret,
		CreatedAt: createdAt,
		UpdatedAt: updatedAt,
	}

	err = i.persister.GetOTPSecretPersisterWithConnection(i.tx).Create(otpSecretModel)
	return err
}


================================================
FILE: backend/cmd/user/root.go
================================================
package user

import (
	"github.com/spf13/cobra"
)

func NewUserCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "user",
		Short: "User import/export tools",
		Long:  `Add the ability to import/export users into/from the hanko database.`,
	}
}

func RegisterCommands(parent *cobra.Command) {
	command := NewUserCommand()
	parent.AddCommand(command)
	command.AddCommand(NewImportCommand())
	command.AddCommand(NewGenerateCommand())
	command.AddCommand(NewExportCommand())
}


================================================
FILE: backend/cmd/version/version.go
================================================
package version

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/teamhanko/hanko/backend/v2/build_info"
)

func NewVersionCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "version",
		Short: "Print the version and exit",
		Long: `Prints the version of the hanko binary.
For all non 'clean' semver tags (e.g. vX.Y.Z) the format is the following: vX.Y.Z-CC-CH[-dirty].
vX.Y.Z: the last tagged semver tag
CC: Commits since the last tag
CH: The commit short hash of the current commit
[-dirty]: is appended if there are any changes that are not committed yet`,
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println(build_info.GetVersion())
		},
	}
}

func RegisterCommands(parent *cobra.Command) {
	cmd := NewVersionCommand()
	parent.AddCommand(cmd)
}


================================================
FILE: backend/config/config.go
================================================
package config

import (
	"fmt"
	"log"

	"github.com/kelseyhightower/envconfig"
	"github.com/knadh/koanf/parsers/yaml"
	"github.com/knadh/koanf/providers/file"
	"github.com/knadh/koanf/v2"
	"github.com/teamhanko/hanko/backend/v2/ee/saml/config"
)

// Config is the central configuration type
type Config struct {
	// `account` configures settings related to user accounts.
	Account Account `yaml:"account" json:"account,omitempty" koanf:"account" jsonschema:"title=account"`
	// `audit_log` configures output and storage modalities of audit logs.
	AuditLog AuditLog `yaml:"audit_log" json:"audit_log,omitempty" koanf:"audit_log" split_words:"true" jsonschema:"title=audit_log"`
	// `convert_legacy_config`, if set to `true`, automatically copies the set values of deprecated configurati
Download .txt
gitextract_n6eqdkjy/

├── .editorconfig
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── BUG_REPORT.yml
│   │   ├── FEATURE_REQUEST.yml
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── build-frontend.yml
│       ├── cli-publish.yml
│       ├── codeql-analysis.yml
│       ├── docker-publish.yml
│       ├── e2e.yml
│       ├── go.yml
│       ├── release-frontend-sdk.yml
│       ├── release-hanko-elements.yml
│       ├── schema-generate-config.yml
│       ├── schema-generate-import.yml
│       ├── schema-markdown-config.yml
│       └── schema-markdown-import.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── backend/
│   ├── .goreleaser.yaml
│   ├── Dockerfile
│   ├── Dockerfile.debug
│   ├── README.md
│   ├── audit_log/
│   │   └── logger.go
│   ├── build_info/
│   │   └── build_info.go
│   ├── cmd/
│   │   ├── cleanup/
│   │   │   └── cleanup.go
│   │   ├── isready/
│   │   │   └── isready.go
│   │   ├── jwk/
│   │   │   ├── create.go
│   │   │   └── root.go
│   │   ├── jwt/
│   │   │   ├── create.go
│   │   │   └── root.go
│   │   ├── migrate/
│   │   │   ├── down.go
│   │   │   ├── root.go
│   │   │   └── up.go
│   │   ├── root.go
│   │   ├── schema/
│   │   │   ├── generate.go
│   │   │   ├── generate_config.go
│   │   │   ├── generate_import.go
│   │   │   ├── markdown.go
│   │   │   ├── markdown_config.go
│   │   │   ├── markdown_import.go
│   │   │   └── root.go
│   │   ├── serve/
│   │   │   ├── admin.go
│   │   │   ├── all.go
│   │   │   ├── public.go
│   │   │   └── root.go
│   │   ├── siwa/
│   │   │   └── siwa.go
│   │   ├── user/
│   │   │   ├── export.go
│   │   │   ├── format.go
│   │   │   ├── format_test.go
│   │   │   ├── generate.go
│   │   │   ├── import.go
│   │   │   ├── import_test.go
│   │   │   ├── importer.go
│   │   │   └── root.go
│   │   └── version/
│   │       └── version.go
│   ├── config/
│   │   ├── config.go
│   │   ├── config.yaml
│   │   ├── config_account.go
│   │   ├── config_audit_log.go
│   │   ├── config_database.go
│   │   ├── config_default.go
│   │   ├── config_email.go
│   │   ├── config_email_delivery.go
│   │   ├── config_emails.go
│   │   ├── config_flow_locker.go
│   │   ├── config_logger.go
│   │   ├── config_mfa.go
│   │   ├── config_passcode.go
│   │   ├── config_passkey.go
│   │   ├── config_password.go
│   │   ├── config_privacy.go
│   │   ├── config_rate_limiter.go
│   │   ├── config_secrets.go
│   │   ├── config_security_notifications.go
│   │   ├── config_server.go
│   │   ├── config_service.go
│   │   ├── config_session.go
│   │   ├── config_shared.go
│   │   ├── config_test.go
│   │   ├── config_third_party.go
│   │   ├── config_username.go
│   │   ├── config_webauthn.go
│   │   ├── config_webhook.go
│   │   ├── config_webhook_test.go
│   │   ├── minimal-config.yaml
│   │   ├── passcode-smtp-config.yaml
│   │   ├── root-passcode-smtp-config.yaml
│   │   └── security-notifications-disabled-config.yaml
│   ├── crypto/
│   │   ├── aes_gcm/
│   │   │   ├── aes_gcm.go
│   │   │   └── aes_gcm_test.go
│   │   ├── jwk/
│   │   │   ├── aws_kms/
│   │   │   │   ├── adapter.go
│   │   │   │   └── manager.go
│   │   │   ├── local_db/
│   │   │   │   ├── generator.go
│   │   │   │   ├── generator_rsa.go
│   │   │   │   ├── generator_test.go
│   │   │   │   ├── manager.go
│   │   │   │   └── manager_test.go
│   │   │   ├── manager.go
│   │   │   └── types.go
│   │   ├── passcode.go
│   │   ├── passcode_test.go
│   │   └── string.go
│   ├── dto/
│   │   ├── admin/
│   │   │   ├── email.go
│   │   │   ├── identity.go
│   │   │   ├── metadata.go
│   │   │   ├── otp.go
│   │   │   ├── password.go
│   │   │   ├── session.go
│   │   │   ├── user.go
│   │   │   ├── username.go
│   │   │   ├── webauthn.go
│   │   │   └── webhook.go
│   │   ├── config.go
│   │   ├── email.go
│   │   ├── error_handler.go
│   │   ├── intern/
│   │   │   ├── WebauthnCredential.go
│   │   │   ├── WebauthnSessionData.go
│   │   │   └── WebauthnUser.go
│   │   ├── metadata.go
│   │   ├── passcode.go
│   │   ├── profile.go
│   │   ├── session.go
│   │   ├── session_test.go
│   │   ├── thirdparty.go
│   │   ├── user.go
│   │   ├── username.go
│   │   ├── validator.go
│   │   ├── webauthn.go
│   │   └── webhook/
│   │       └── email.go
│   ├── ee/
│   │   └── saml/
│   │       ├── config/
│   │       │   ├── saml.go
│   │       │   └── saml_test.go
│   │       ├── dto/
│   │       │   └── saml.go
│   │       ├── handler.go
│   │       ├── provider/
│   │       │   ├── auth0.go
│   │       │   ├── provider.go
│   │       │   └── saml.go
│   │       ├── router.go
│   │       ├── service.go
│   │       ├── state.go
│   │       ├── state_test.go
│   │       └── utils/
│   │           ├── response.go
│   │           └── url.go
│   ├── flow_api/
│   │   ├── flow/
│   │   │   ├── capabilities/
│   │   │   │   └── action_send_capabilities.go
│   │   │   ├── credential_onboarding/
│   │   │   │   ├── action_continue_to_passkey.go
│   │   │   │   ├── action_continue_to_password.go
│   │   │   │   ├── action_register_password.go
│   │   │   │   ├── action_skip_method_chooser.go
│   │   │   │   ├── action_skip_passkey.go
│   │   │   │   ├── action_skip_password.go
│   │   │   │   ├── action_webauthn_generate_creation_options.go
│   │   │   │   └── action_webauthn_verify_attestation_response.go
│   │   │   ├── credential_usage/
│   │   │   │   ├── action_continue_to_passcode_confirmation.go
│   │   │   │   ├── action_continue_to_passcode_confirmation_recovery.go
│   │   │   │   ├── action_continue_to_password_login.go
│   │   │   │   ├── action_continue_with_login_identifier.go
│   │   │   │   ├── action_password_login.go
│   │   │   │   ├── action_password_recovery.go
│   │   │   │   ├── action_remember_me.go
│   │   │   │   ├── action_resend_passcode.go
│   │   │   │   ├── action_verify_passcode.go
│   │   │   │   ├── action_webauthn_generate_request_options.go
│   │   │   │   ├── action_webauthn_verify_assertion_response.go
│   │   │   │   └── hook_send_passcode.go
│   │   │   ├── device_trust/
│   │   │   │   ├── action_trust_device.go
│   │   │   │   ├── hook_issue_trust_device_cookie.go
│   │   │   │   └── hook_schedule_trust_device_state.go
│   │   │   ├── flows.go
│   │   │   ├── login/
│   │   │   │   ├── hook_create_email.go
│   │   │   │   ├── hook_schedule_onboarding_states.go
│   │   │   │   ├── hook_trigger_login_webhook.go
│   │   │   │   └── hook_webauthn_generate_request_options_cond.go
│   │   │   ├── mfa_creation/
│   │   │   │   ├── action_continue_to_otp_secret_creation.go
│   │   │   │   ├── action_continue_to_security_key_creation.go
│   │   │   │   ├── action_otp_code_verify.go
│   │   │   │   ├── action_webauthn_generate_creation_options_for_security_keys.go
│   │   │   │   ├── hook_otp_secret_generate.go
│   │   │   │   └── skip_mfa.go
│   │   │   ├── mfa_usage/
│   │   │   │   ├── action_continue_to_login_otp.go
│   │   │   │   ├── action_continue_to_login_security_key.go
│   │   │   │   ├── action_otp_code_validate.go
│   │   │   │   └── action_webauthn_generate_request_options_security_key.go
│   │   │   ├── profile/
│   │   │   │   ├── action_account_delete.go
│   │   │   │   ├── action_connect_thirdparty_oauth_provider.go
│   │   │   │   ├── action_continue_to_otp_secret_creation.go
│   │   │   │   ├── action_continue_to_security_key_creation.go
│   │   │   │   ├── action_disconnect_thirdparty_oauth_provider.go
│   │   │   │   ├── action_email_create.go
│   │   │   │   ├── action_email_delete.go
│   │   │   │   ├── action_email_set_primary.go
│   │   │   │   ├── action_email_verify.go
│   │   │   │   ├── action_exchange_token.go
│   │   │   │   ├── action_otp_secret_delete.go
│   │   │   │   ├── action_password_create.go
│   │   │   │   ├── action_password_delete.go
│   │   │   │   ├── action_password_update.go
│   │   │   │   ├── action_patch_metadata.go
│   │   │   │   ├── action_security_key_create.go
│   │   │   │   ├── action_security_key_delete.go
│   │   │   │   ├── action_session_delete.go
│   │   │   │   ├── action_username_create.go
│   │   │   │   ├── action_username_delete.go
│   │   │   │   ├── action_username_update.go
│   │   │   │   ├── action_webauthn_credential_create.go
│   │   │   │   ├── action_webauthn_credential_delete.go
│   │   │   │   ├── action_webauthn_credential_rename.go
│   │   │   │   ├── action_webauthn_verify_attestation_response.go
│   │   │   │   ├── hook_get_profile_data.go
│   │   │   │   ├── hook_get_sessions.go
│   │   │   │   └── hook_refresh_session_user.go
│   │   │   ├── registration/
│   │   │   │   ├── action_register_login_identifier.go
│   │   │   │   ├── hook_create_user.go
│   │   │   │   └── hook_determine_amr_values.go
│   │   │   ├── shared/
│   │   │   │   ├── action_back.go
│   │   │   │   ├── action_exchange_token.go
│   │   │   │   ├── action_skip.go
│   │   │   │   ├── action_thirdparty_oauth.go
│   │   │   │   ├── const_action_names.go
│   │   │   │   ├── const_email_templates.go
│   │   │   │   ├── const_flow_names.go
│   │   │   │   ├── const_stash_paths.go
│   │   │   │   ├── const_state_names.go
│   │   │   │   ├── errors.go
│   │   │   │   ├── flow.go
│   │   │   │   ├── hook_determine_amr_values.go
│   │   │   │   ├── hook_email_persist_verified_status.go
│   │   │   │   ├── hook_generate_oauth_links.go
│   │   │   │   ├── hook_get_user_data.go
│   │   │   │   ├── hook_issue_session.go
│   │   │   │   ├── hook_password_save.go
│   │   │   │   ├── hook_persist_webauthn_credential.go
│   │   │   │   ├── hook_schedule_mfa_creation_states.go
│   │   │   │   ├── hook_verify_attestation_response.go
│   │   │   │   └── links.go
│   │   │   └── user_details/
│   │   │       ├── action_set_email.go
│   │   │       ├── action_set_username.go
│   │   │       ├── action_skip_email.go
│   │   │       └── action_skip_username.go
│   │   ├── flow_locker/
│   │   │   ├── flow_locker.go
│   │   │   ├── flow_locker_test.go
│   │   │   ├── memory.go
│   │   │   ├── memory_test.go
│   │   │   ├── noop.go
│   │   │   ├── redis.go
│   │   │   └── redis_test.go
│   │   ├── handler.go
│   │   ├── services/
│   │   │   ├── device_trust.go
│   │   │   ├── email.go
│   │   │   ├── passcode.go
│   │   │   ├── password.go
│   │   │   ├── security_notification.go
│   │   │   ├── user.go
│   │   │   └── webauthn.go
│   │   └── static/
│   │       └── generic_client.html
│   ├── flowpilot/
│   │   ├── action_input.go
│   │   ├── builder.go
│   │   ├── builder_subflow.go
│   │   ├── context.go
│   │   ├── context_action_exec.go
│   │   ├── context_action_init.go
│   │   ├── context_flow.go
│   │   ├── db.go
│   │   ├── errors.go
│   │   ├── flow.go
│   │   ├── input.go
│   │   ├── input_allowed_value.go
│   │   ├── input_schema.go
│   │   ├── jsonmanager/
│   │   │   └── manager.go
│   │   ├── link.go
│   │   ├── payload.go
│   │   ├── query_param.go
│   │   ├── random.go
│   │   ├── response.go
│   │   ├── stash.go
│   │   ├── state_action.go
│   │   └── state_detail.go
│   ├── go.mod
│   ├── go.sum
│   ├── handler/
│   │   ├── admin_router.go
│   │   ├── audit_log.go
│   │   ├── email.go
│   │   ├── email_admin.go
│   │   ├── email_admin_test.go
│   │   ├── email_test.go
│   │   ├── health.go
│   │   ├── health_test.go
│   │   ├── helpers_test.go
│   │   ├── metadata_admin.go
│   │   ├── metadata_admin_test.go
│   │   ├── otp_admin.go
│   │   ├── otp_admin_test.go
│   │   ├── passcode.go
│   │   ├── passcode_test.go
│   │   ├── password.go
│   │   ├── password_admin.go
│   │   ├── password_admin_test.go
│   │   ├── password_test.go
│   │   ├── public_router.go
│   │   ├── session.go
│   │   ├── session_admin.go
│   │   ├── session_admin_test.go
│   │   ├── status.go
│   │   ├── thirdparty.go
│   │   ├── thirdparty_auth_test.go
│   │   ├── thirdparty_callback_error_test.go
│   │   ├── thirdparty_callback_test.go
│   │   ├── thirdparty_test.go
│   │   ├── token.go
│   │   ├── token_test.go
│   │   ├── user.go
│   │   ├── user_admin.go
│   │   ├── user_admin_test.go
│   │   ├── user_test.go
│   │   ├── utils.go
│   │   ├── webauthn.go
│   │   ├── webauthn_credential_admin.go
│   │   ├── webauthn_credential_admin_test.go
│   │   ├── webauthn_test.go
│   │   ├── webhook.go
│   │   ├── webhook_test.go
│   │   ├── well_known.go
│   │   └── well_known_test.go
│   ├── json_schema/
│   │   ├── hanko.config.json
│   │   └── hanko.user_import.json
│   ├── mail/
│   │   ├── locales/
│   │   │   ├── passcode.bn.yaml
│   │   │   ├── passcode.de.yaml
│   │   │   ├── passcode.en.yaml
│   │   │   ├── passcode.fr.yaml
│   │   │   ├── passcode.it.yaml
│   │   │   ├── passcode.nl.yaml
│   │   │   ├── passcode.pt-BR.yaml
│   │   │   ├── passcode.zh-CN.yaml
│   │   │   ├── security-notifications.bn.yaml
│   │   │   ├── security-notifications.de.yaml
│   │   │   ├── security-notifications.en.yaml
│   │   │   ├── security-notifications.fr.yaml
│   │   │   ├── security-notifications.it.yaml
│   │   │   ├── security-notifications.nl.yaml
│   │   │   ├── security-notifications.pt-BR.yaml
│   │   │   └── security-notifications.zh-CN.yaml
│   │   ├── mailer.go
│   │   ├── mailer_test.go
│   │   ├── render.go
│   │   ├── render_test.go
│   │   └── templates/
│   │       ├── email_create.html.tmpl
│   │       ├── email_create.txt.tmpl
│   │       ├── email_delete.html.tmpl
│   │       ├── email_delete.txt.tmpl
│   │       ├── email_login_attempted.html.tmpl
│   │       ├── email_login_attempted.txt.tmpl
│   │       ├── email_registration_attempted.html.tmpl
│   │       ├── email_registration_attempted.txt.tmpl
│   │       ├── email_verification.html.tmpl
│   │       ├── email_verification.txt.tmpl
│   │       ├── layout.html.tmpl
│   │       ├── login.html.tmpl
│   │       ├── login.txt.tmpl
│   │       ├── mfa_create.html.tmpl
│   │       ├── mfa_create.txt.tmpl
│   │       ├── mfa_delete.html.tmpl
│   │       ├── mfa_delete.txt.tmpl
│   │       ├── passkey_create.html.tmpl
│   │       ├── passkey_create.txt.tmpl
│   │       ├── password_update.html.tmpl
│   │       ├── password_update.txt.tmpl
│   │       ├── primary_email_update.html.tmpl
│   │       ├── primary_email_update.txt.tmpl
│   │       ├── recovery.html.tmpl
│   │       └── recovery.txt.tmpl
│   ├── main.go
│   ├── mapper/
│   │   ├── aaguid.json
│   │   └── authenticator_mapper.go
│   ├── middleware/
│   │   ├── logger.go
│   │   ├── session.go
│   │   └── webhook.go
│   ├── pagination/
│   │   ├── header.go
│   │   └── header_test.go
│   ├── persistence/
│   │   ├── audit_log_persister.go
│   │   ├── email_persister.go
│   │   ├── flow_persister.go
│   │   ├── identity_persister.go
│   │   ├── jwk_persister.go
│   │   ├── migrations/
│   │   │   ├── 20220405153240_create_users.down.fizz
│   │   │   ├── 20220405153240_create_users.up.fizz
│   │   │   ├── 20220405153750_create_passcodes.down.fizz
│   │   │   ├── 20220405153750_create_passcodes.up.fizz
│   │   │   ├── 20220405154240_create_webauthn_credentials.down.fizz
│   │   │   ├── 20220405154240_create_webauthn_credentials.up.fizz
│   │   │   ├── 20220405154750_create_webauthn_session_data.down.fizz
│   │   │   ├── 20220405154750_create_webauthn_session_data.up.fizz
│   │   │   ├── 20220405155120_create_webauthn_session_data_allowed_credentials.down.fizz
│   │   │   ├── 20220405155120_create_webauthn_session_data_allowed_credentials.up.fizz
│   │   │   ├── 20220413152500_create_jwk.down.fizz
│   │   │   ├── 20220413152500_create_jwk.up.fizz
│   │   │   ├── 20220425122015_create_password_credentials.down.fizz
│   │   │   ├── 20220425122015_create_password_credentials.up.fizz
│   │   │   ├── 20220711121022_create_credential_transports.down.fizz
│   │   │   ├── 20220711121022_create_credential_transports.up.fizz
│   │   │   ├── 20220818111000_create_audit_logs.down.fizz
│   │   │   ├── 20220818111000_create_audit_logs.up.fizz
│   │   │   ├── 20221027104800_create_emails.down.fizz
│   │   │   ├── 20221027104800_create_emails.up.fizz
│   │   │   ├── 20221027104900_change_users.down.fizz
│   │   │   ├── 20221027104900_change_users.up.fizz
│   │   │   ├── 20221027123530_change_passcodes.down.fizz
│   │   │   ├── 20221027123530_change_passcodes.up.fizz
│   │   │   ├── 20221222134900_change_webauthn_credentials.down.fizz
│   │   │   ├── 20221222134900_change_webauthn_credentials.up.fizz
│   │   │   ├── 20230112152816_create_identities.down.fizz
│   │   │   ├── 20230112152816_create_identities.up.fizz
│   │   │   ├── 20230206102000_change_webauthn_credentials.down.fizz
│   │   │   ├── 20230206102000_change_webauthn_credentials.up.fizz
│   │   │   ├── 20230222114100_change_webauthn_credentials.down.fizz
│   │   │   ├── 20230222114100_change_webauthn_credentials.up.fizz
│   │   │   ├── 20230317114334_create_tokens.down.fizz
│   │   │   ├── 20230317114334_create_tokens.up.fizz
│   │   │   ├── 20230801124808_webauthn_session_data_add_expiry.down.fizz
│   │   │   ├── 20230801124808_webauthn_session_data_add_expiry.up.fizz
│   │   │   ├── 20230810173315_create_flows.down.fizz
│   │   │   ├── 20230810173315_create_flows.up.fizz
│   │   │   ├── 20230905102601_create_saml_state.down.fizz
│   │   │   ├── 20230905102601_create_saml_state.up.fizz
│   │   │   ├── 20230915111552_create_saml_certs.down.fizz
│   │   │   ├── 20230915111552_create_saml_certs.up.fizz
│   │   │   ├── 20231012141100_change_user_table.down.fizz
│   │   │   ├── 20231012141100_change_user_table.up.fizz
│   │   │   ├── 20231013113800_change_passcode_table.down.fizz
│   │   │   ├── 20231013113800_change_passcode_table.up.fizz
│   │   │   ├── 20240108094151_create_webhooks.down.fizz
│   │   │   ├── 20240108094151_create_webhooks.up.fizz
│   │   │   ├── 20240108094210_create_webhook_events.down.fizz
│   │   │   ├── 20240108094210_create_webhook_events.up.fizz
│   │   │   ├── 20240207150616_change_audit_logs.down.fizz
│   │   │   ├── 20240207150616_change_audit_logs.up.fizz
│   │   │   ├── 20240530122100_change_tokens.down.fizz
│   │   │   ├── 20240530122100_change_tokens.up.fizz
│   │   │   ├── 20240530145724_change_users.down.fizz
│   │   │   ├── 20240530145724_change_users.up.fizz
│   │   │   ├── 20240612122326_change_flows.down.fizz
│   │   │   ├── 20240612122326_change_flows.up.fizz
│   │   │   ├── 20240717020131_drop_transitions.down.fizz
│   │   │   ├── 20240717020131_drop_transitions.up.fizz
│   │   │   ├── 20240717020707_change_flows.down.fizz
│   │   │   ├── 20240717020707_change_flows.up.fizz
│   │   │   ├── 20240723171257_change_passcodes.down.fizz
│   │   │   ├── 20240723171257_change_passcodes.up.fizz
│   │   │   ├── 20240723173648_create_usernames.down.fizz
│   │   │   ├── 20240723173648_create_usernames.up.fizz
│   │   │   ├── 20240826132046_create_otp_secrets.down.fizz
│   │   │   ├── 20240826132046_create_otp_secrets.up.fizz
│   │   │   ├── 20240826133417_change_webauthn_credentials.down.fizz
│   │   │   ├── 20240826133417_change_webauthn_credentials.up.fizz
│   │   │   ├── 20241002113000_create_sessions.down.fizz
│   │   │   ├── 20241002113000_create_sessions.up.fizz
│   │   │   ├── 20241106171500_change_sessions.down.fizz
│   │   │   ├── 20241106171500_change_sessions.up.fizz
│   │   │   ├── 20241112181011_create_trusted_devices.down.fizz
│   │   │   ├── 20241112181011_create_trusted_devices.up.fizz
│   │   │   ├── 20241118114500_change_webauthn_credentials.down.fizz
│   │   │   ├── 20241118114500_change_webauthn_credentials.up.fizz
│   │   │   ├── 20250130154010_change_identities.down.fizz
│   │   │   ├── 20250130154010_change_identities.up.fizz
│   │   │   ├── 20250130170131_create_saml_identities.down.fizz
│   │   │   ├── 20250130170131_create_saml_identities.up.fizz
│   │   │   ├── 20250210095906_create_saml_idp_initiated_requests.down.fizz
│   │   │   ├── 20250210095906_create_saml_idp_initiated_requests.up.fizz
│   │   │   ├── 20250313160348_change_flows.down.fizz
│   │   │   ├── 20250313160348_change_flows.up.fizz
│   │   │   ├── 20250414165334_create_user_metadata.down.fizz
│   │   │   ├── 20250414165334_create_user_metadata.up.fizz
│   │   │   ├── 20251002113000_change_tokens.down.fizz
│   │   │   ├── 20251002113000_change_tokens.up.fizz
│   │   │   ├── 20251104000000_add_user_id_to_identities.down.fizz
│   │   │   ├── 20251104000000_add_user_id_to_identities.up.fizz
│   │   │   ├── 20251119000000_change_tokens.down.fizz
│   │   │   ├── 20251119000000_change_tokens.up.fizz
│   │   │   ├── 20260204151021_change_user.down.fizz
│   │   │   └── 20260204151021_change_user.up.fizz
│   │   ├── models/
│   │   │   ├── audit_log.go
│   │   │   ├── email.go
│   │   │   ├── flow.go
│   │   │   ├── identity.go
│   │   │   ├── jwk.go
│   │   │   ├── otp_secret.go
│   │   │   ├── passcode.go
│   │   │   ├── password_credential.go
│   │   │   ├── primary_email.go
│   │   │   ├── saml_certificate.go
│   │   │   ├── saml_identity.go
│   │   │   ├── saml_idp_initiated_request.go
│   │   │   ├── saml_state.go
│   │   │   ├── session.go
│   │   │   ├── token.go
│   │   │   ├── trusted_device.go
│   │   │   ├── user.go
│   │   │   ├── user_metadata.go
│   │   │   ├── username.go
│   │   │   ├── webauthn_credential.go
│   │   │   ├── webauthn_credential_transport.go
│   │   │   ├── webauthn_credential_user_handle.go
│   │   │   ├── webauthn_session_data.go
│   │   │   ├── webauthn_session_data_allowed_credential.go
│   │   │   ├── webhook.go
│   │   │   └── webhook_event.go
│   │   ├── otp_secret_persister.go
│   │   ├── passcode_persister.go
│   │   ├── password_credential_persister.go
│   │   ├── persister.go
│   │   ├── primary_email_persister.go
│   │   ├── saml_certificate_persister.go
│   │   ├── saml_identity_persister.go
│   │   ├── saml_idp_inititated_request_persister.go
│   │   ├── saml_state_persister.go
│   │   ├── session_persister.go
│   │   ├── token_persister.go
│   │   ├── trusted_device_persister.go
│   │   ├── user_metadata_persister.go
│   │   ├── user_persister.go
│   │   ├── username_persister.go
│   │   ├── webauthn_credential_persister.go
│   │   ├── webauthn_credential_user_handle_persister.go
│   │   ├── webauthn_session_data_persister.go
│   │   └── webhook_persister.go
│   ├── rate_limiter/
│   │   ├── rate_limiter.go
│   │   └── rate_limiter_test.go
│   ├── server/
│   │   └── server.go
│   ├── session/
│   │   ├── session.go
│   │   ├── session_test.go
│   │   ├── template.go
│   │   └── template_test.go
│   ├── template/
│   │   ├── template.go
│   │   └── templates/
│   │       └── status.tmpl
│   ├── test/
│   │   ├── audit_logger.go
│   │   ├── config.go
│   │   ├── database.go
│   │   ├── fixtures/
│   │   │   ├── actions/
│   │   │   │   ├── get_wa_creation_options/
│   │   │   │   │   └── flows.yaml
│   │   │   │   ├── send_capabilities/
│   │   │   │   │   └── flows.yaml
│   │   │   │   ├── send_wa_attestation_response/
│   │   │   │   │   ├── flows.yaml
│   │   │   │   │   └── webauthn_session_data.yaml
│   │   │   │   ├── submit_new_password/
│   │   │   │   │   └── flows.yaml
│   │   │   │   ├── submit_passcode/
│   │   │   │   │   ├── flows.yaml
│   │   │   │   │   └── passcodes.yaml
│   │   │   │   └── submit_registration_identifier/
│   │   │   │       ├── emails.yaml
│   │   │   │       ├── flows.yaml
│   │   │   │       └── users.yaml
│   │   │   ├── email/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── metadata/
│   │   │   │   ├── user_metadata.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── otp/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── otp_secrets.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── passcode/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── password/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── password_credentials.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── saml_state/
│   │   │   │   └── saml_states.yaml
│   │   │   ├── sessions/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   ├── sessions.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── thirdparty/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── identities.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── token/
│   │   │   │   ├── tokens.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── user/
│   │   │   │   ├── emails.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── user_admin/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── usernames.yaml
│   │   │   │   └── users.yaml
│   │   │   ├── user_with_webauthn_credential/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── identities.yaml
│   │   │   │   ├── user_metadata.yaml
│   │   │   │   ├── usernames.yaml
│   │   │   │   ├── users.yaml
│   │   │   │   └── webauthn_credentials.yaml
│   │   │   ├── webauthn/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   ├── users.yaml
│   │   │   │   ├── webauthn_credentials.yaml
│   │   │   │   └── webauthn_session_data.yaml
│   │   │   ├── webauthn_registration/
│   │   │   │   ├── emails.yaml
│   │   │   │   ├── primary_emails.yaml
│   │   │   │   ├── users.yaml
│   │   │   │   ├── webauthn_credentials.yaml
│   │   │   │   └── webauthn_session_data.yaml
│   │   │   └── webhooks/
│   │   │       ├── webhook_events.yaml
│   │   │       └── webhooks.yaml
│   │   ├── jwk_manager.go
│   │   ├── mailslurper.go
│   │   └── suite.go
│   ├── thirdparty/
│   │   ├── claims.go
│   │   ├── error.go
│   │   ├── helper.go
│   │   ├── helper_test.go
│   │   ├── linking.go
│   │   ├── provider.go
│   │   ├── provider_apple.go
│   │   ├── provider_custom.go
│   │   ├── provider_discord.go
│   │   ├── provider_facebook.go
│   │   ├── provider_github.go
│   │   ├── provider_google.go
│   │   ├── provider_linkedin.go
│   │   ├── provider_microsoft.go
│   │   ├── state.go
│   │   └── state_test.go
│   ├── utils/
│   │   ├── cookie.go
│   │   ├── cookie_test.go
│   │   ├── mask.go
│   │   ├── mask_test.go
│   │   ├── url.go
│   │   └── url_test.go
│   └── webhooks/
│       ├── config_hook.go
│       ├── config_hook_test.go
│       ├── database_hook.go
│       ├── database_hook_test.go
│       ├── events/
│       │   └── events.go
│       ├── manager.go
│       ├── manager_test.go
│       ├── utils/
│       │   ├── webhook.go
│       │   └── webhook_test.go
│       ├── webhook.go
│       ├── webhook_test.go
│       ├── worker.go
│       └── worker_test.go
├── deploy/
│   ├── docker-compose/
│   │   ├── base.yaml
│   │   ├── config-disable-signup.yaml
│   │   ├── config-rate-limiting.yaml
│   │   ├── config.yaml
│   │   ├── quickstart-with-redis.yaml
│   │   ├── quickstart.debug.yaml
│   │   ├── quickstart.e2e.yaml
│   │   ├── quickstart.yaml
│   │   ├── todo-angular.yaml
│   │   ├── todo-nextjs.yaml
│   │   ├── todo-react.yaml
│   │   ├── todo-svelte.yaml
│   │   └── todo-vue.yaml
│   └── k8s/
│       ├── README.md
│       ├── base/
│       │   ├── elements/
│       │   │   ├── deployment.yaml
│       │   │   ├── ingress.yaml
│       │   │   ├── kustomization.yaml
│       │   │   └── service.yaml
│       │   ├── mailhog/
│       │   │   ├── deployment.yaml
│       │   │   ├── ingress.yaml
│       │   │   ├── kustomization.yaml
│       │   │   └── service.yaml
│       │   ├── postgres/
│       │   │   ├── deployment.yaml
│       │   │   ├── initdbscript.sh
│       │   │   ├── kustomization.yaml
│       │   │   ├── persistent-volume.yaml
│       │   │   └── service.yaml
│       │   └── quickstart/
│       │       ├── deployment.yaml
│       │       ├── ingress.yaml
│       │       ├── kustomization.yaml
│       │       └── service.yaml
│       └── overlays/
│           ├── quickstart/
│           │   └── kustomization.yaml
│           └── thirdparty-x-domain/
│               ├── README.md
│               ├── config.yaml
│               ├── env-patch.yaml
│               ├── ingress-patch.yaml
│               └── kustomization.yaml
├── e2e/
│   ├── .eslintignore
│   ├── .eslintrc.cjs
│   ├── .nvmrc
│   ├── README.md
│   ├── fixtures/
│   │   └── Pages.ts
│   ├── global.d.ts
│   ├── helper/
│   │   ├── Accounts.ts
│   │   ├── Endpoints.ts
│   │   ├── MailSlurper.ts
│   │   ├── Matchers.ts
│   │   └── Setup.ts
│   ├── package.json
│   ├── pages/
│   │   ├── BasePage.ts
│   │   ├── Error.ts
│   │   ├── LoginEmail.ts
│   │   ├── LoginEmailNoSignUp.ts
│   │   ├── LoginPasscode.ts
│   │   ├── LoginPassword.ts
│   │   ├── NoAccountFound.ts
│   │   ├── RegisterAuthenticator.ts
│   │   ├── RegisterConfirm.ts
│   │   ├── RegisterPassword.ts
│   │   └── SecuredContent.ts
│   ├── playwright.config.ts
│   ├── seed/
│   │   ├── Dockerfile
│   │   ├── init.sh
│   │   └── seed.sql
│   ├── tests/
│   │   ├── common.spec.ts
│   │   ├── nosignup.spec.ts
│   │   ├── passwordless.spec.ts
│   │   └── passwords.spec.ts
│   └── tsconfig.json
├── frontend/
│   ├── .dockerignore
│   ├── Dockerfile
│   ├── Dockerfile.debug
│   ├── elements/
│   │   ├── .dockerignore
│   │   ├── .eslintignore
│   │   ├── .eslintrc.cjs
│   │   ├── .nvmrc
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── babel.config.cjs
│   │   ├── example.css
│   │   ├── nginx/
│   │   │   └── default.conf
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── Elements.tsx
│   │   │   ├── _mixins.sass
│   │   │   ├── _preset.sass
│   │   │   ├── _variables.sass
│   │   │   ├── components/
│   │   │   │   ├── accordion/
│   │   │   │   │   ├── Accordion.tsx
│   │   │   │   │   ├── AddEmailDropdown.tsx
│   │   │   │   │   ├── AddWebauthnCredentialDropdown.tsx
│   │   │   │   │   ├── ChangePasswordDropdown.tsx
│   │   │   │   │   ├── ChangeUsernameDropdown.tsx
│   │   │   │   │   ├── ConnectIdentityDropdown.tsx
│   │   │   │   │   ├── Dropdown.tsx
│   │   │   │   │   ├── ListEmailsAccordion.tsx
│   │   │   │   │   ├── ListIdentities.tsx
│   │   │   │   │   ├── ListSessionsAccordion.tsx
│   │   │   │   │   ├── ListWebauthnCredentialsAccordion.tsx
│   │   │   │   │   ├── ManageAuthAppDropdown.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── error/
│   │   │   │   │   ├── ErrorBox.tsx
│   │   │   │   │   ├── ErrorMessage.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── form/
│   │   │   │   │   ├── Button.tsx
│   │   │   │   │   ├── Checkbox.tsx
│   │   │   │   │   ├── CodeInput.tsx
│   │   │   │   │   ├── Form.tsx
│   │   │   │   │   ├── Input.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── headline/
│   │   │   │   │   ├── Headline1.tsx
│   │   │   │   │   ├── Headline2.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── icons/
│   │   │   │   │   ├── Apple.tsx
│   │   │   │   │   ├── Checkmark.tsx
│   │   │   │   │   ├── Copy.tsx
│   │   │   │   │   ├── CustomProvider.tsx
│   │   │   │   │   ├── Discord.tsx
│   │   │   │   │   ├── ExclamationMark.tsx
│   │   │   │   │   ├── Facebook.tsx
│   │   │   │   │   ├── GitHub.tsx
│   │   │   │   │   ├── Google.tsx
│   │   │   │   │   ├── Icon.tsx
│   │   │   │   │   ├── LinkedIn.tsx
│   │   │   │   │   ├── LoadingSpinner.tsx
│   │   │   │   │   ├── Mail.tsx
│   │   │   │   │   ├── Microsoft.tsx
│   │   │   │   │   ├── Passkey.tsx
│   │   │   │   │   ├── Password.tsx
│   │   │   │   │   ├── QRCodeScanner.tsx
│   │   │   │   │   ├── SecurityKey.tsx
│   │   │   │   │   ├── Spinner.tsx
│   │   │   │   │   ├── icons.ts
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── link/
│   │   │   │   │   ├── Link.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── otp/
│   │   │   │   │   ├── OTPCreationDetails.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── paragraph/
│   │   │   │   │   ├── Paragraph.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   ├── spacer/
│   │   │   │   │   ├── Divider.tsx
│   │   │   │   │   ├── Spacer.tsx
│   │   │   │   │   └── styles.sass
│   │   │   │   └── wrapper/
│   │   │   │       ├── Clipboard.tsx
│   │   │   │       ├── Container.tsx
│   │   │   │       ├── Content.tsx
│   │   │   │       ├── Footer.tsx
│   │   │   │       └── styles.sass
│   │   │   ├── contexts/
│   │   │   │   └── AppProvider.tsx
│   │   │   ├── declarations.d.ts
│   │   │   ├── example.html
│   │   │   ├── hooks/
│   │   │   │   ├── UseFlowEffects.ts
│   │   │   │   └── UseFlowState.ts
│   │   │   ├── i18n/
│   │   │   │   ├── all.ts
│   │   │   │   ├── bn.ts
│   │   │   │   ├── de.ts
│   │   │   │   ├── en.ts
│   │   │   │   ├── fr.ts
│   │   │   │   ├── it.ts
│   │   │   │   ├── nl.ts
│   │   │   │   ├── pt-BR.ts
│   │   │   │   ├── translations.ts
│   │   │   │   └── zh.ts
│   │   │   ├── index.ts
│   │   │   └── pages/
│   │   │       ├── CreateEmailPage.tsx
│   │   │       ├── CreateOTPSecretPage.tsx
│   │   │       ├── CreatePasswordPage.tsx
│   │   │       ├── CreateSecurityKeyPage.tsx
│   │   │       ├── CreateUsernamePage.tsx
│   │   │       ├── CredentialOnboardingChooser.tsx
│   │   │       ├── DeleteAccountPage.tsx
│   │   │       ├── DeviceTrustPage.tsx
│   │   │       ├── EditPasswordPage.tsx
│   │   │       ├── ErrorPage.tsx
│   │   │       ├── InitPage.tsx
│   │   │       ├── LoginInitPage.tsx
│   │   │       ├── LoginMethodChooser.tsx
│   │   │       ├── LoginOTPPage.tsx
│   │   │       ├── LoginPasswordPage.tsx
│   │   │       ├── LoginSecurityKeyPage.tsx
│   │   │       ├── MFAMethodChooserPage.tsx
│   │   │       ├── PasscodePage.tsx
│   │   │       ├── ProfilePage.tsx
│   │   │       ├── RegisterPasskeyPage.tsx
│   │   │       ├── RegistrationInitPage.tsx
│   │   │       └── RenameWebauthnCredentialPage.tsx
│   │   ├── tsconfig.json
│   │   ├── webpack.config.cjs
│   │   └── webpack.config.dev.cjs
│   ├── examples/
│   │   ├── README.md
│   │   ├── angular/
│   │   │   ├── .browserslistrc
│   │   │   ├── .dockerignore
│   │   │   ├── .editorconfig
│   │   │   ├── .gitignore
│   │   │   ├── Dockerfile
│   │   │   ├── README.md
│   │   │   ├── angular.json
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── app/
│   │   │   │   │   ├── app-routing.module.ts
│   │   │   │   │   ├── app.component.css
│   │   │   │   │   ├── app.component.html
│   │   │   │   │   ├── app.component.ts
│   │   │   │   │   ├── login/
│   │   │   │   │   │   ├── login.component.html
│   │   │   │   │   │   └── login.component.ts
│   │   │   │   │   ├── modal/
│   │   │   │   │   │   ├── session-expired-modal.component.html
│   │   │   │   │   │   └── session-expired-modal.component.ts
│   │   │   │   │   ├── profile/
│   │   │   │   │   │   ├── profile.component.html
│   │   │   │   │   │   └── profile.component.ts
│   │   │   │   │   ├── services/
│   │   │   │   │   │   ├── hanko.services.ts
│   │   │   │   │   │   └── todo.service.ts
│   │   │   │   │   └── todo/
│   │   │   │   │       ├── todo.component.css
│   │   │   │   │       ├── todo.component.html
│   │   │   │   │       └── todo.component.ts
│   │   │   │   ├── assets/
│   │   │   │   │   └── .gitkeep
│   │   │   │   ├── environments/
│   │   │   │   │   └── environment.ts
│   │   │   │   ├── index.html
│   │   │   │   ├── main.ts
│   │   │   │   ├── polyfills.ts
│   │   │   │   └── styles.css
│   │   │   ├── tsconfig.app.json
│   │   │   └── tsconfig.json
│   │   ├── express/
│   │   │   ├── .dockerignore
│   │   │   ├── .gitignore
│   │   │   ├── Dockerfile
│   │   │   ├── README.md
│   │   │   ├── package.json
│   │   │   └── src/
│   │   │       └── server.js
│   │   ├── nextjs/
│   │   │   ├── .dockerignore
│   │   │   ├── .gitignore
│   │   │   ├── Dockerfile
│   │   │   ├── README.md
│   │   │   ├── components/
│   │   │   │   ├── HankoAuth.tsx
│   │   │   │   ├── HankoProfile.tsx
│   │   │   │   └── SessionExpiredModal.tsx
│   │   │   ├── next.config.js
│   │   │   ├── package.json
│   │   │   ├── pages/
│   │   │   │   ├── _app.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── profile.tsx
│   │   │   │   └── todo.tsx
│   │   │   ├── styles/
│   │   │   │   ├── Todo.module.css
│   │   │   │   └── index.css
│   │   │   ├── tsconfig.json
│   │   │   └── util/
│   │   │       └── TodoClient.ts
│   │   ├── react/
│   │   │   ├── .dockerignore
│   │   │   ├── .gitignore
│   │   │   ├── Dockerfile
│   │   │   ├── README.md
│   │   │   ├── index.html
│   │   │   ├── package.json
│   │   │   ├── public/
│   │   │   │   ├── manifest.json
│   │   │   │   └── robots.txt
│   │   │   ├── src/
│   │   │   │   ├── HankoAuth.tsx
│   │   │   │   ├── HankoProfile.tsx
│   │   │   │   ├── SessionExpiredModal.tsx
│   │   │   │   ├── Todo.module.css
│   │   │   │   ├── Todo.tsx
│   │   │   │   ├── TodoClient.ts
│   │   │   │   ├── index.css
│   │   │   │   ├── index.tsx
│   │   │   │   └── react-app-env.d.ts
│   │   │   ├── tsconfig.app.json
│   │   │   ├── tsconfig.json
│   │   │   ├── tsconfig.node.json
│   │   │   ├── vite-env.d.ts
│   │   │   └── vite.config.js
│   │   └── vue/
│   │       ├── .dockerignore
│   │       ├── .gitignore
│   │       ├── .prettierrc.json
│   │       ├── .vscode/
│   │       │   └── extensions.json
│   │       ├── Dockerfile
│   │       ├── README.md
│   │       ├── env.d.ts
│   │       ├── index.html
│   │       ├── package.json
│   │       ├── src/
│   │       │   ├── App.vue
│   │       │   ├── assets/
│   │       │   │   └── base.css
│   │       │   ├── components/
│   │       │   │   └── SessionExpiredModal.vue
│   │       │   ├── main.ts
│   │       │   ├── router/
│   │       │   │   └── index.ts
│   │       │   ├── utils/
│   │       │   │   └── TodoClient.ts
│   │       │   └── views/
│   │       │       ├── LoginView.vue
│   │       │       ├── ProfileView.vue
│   │       │       └── TodoView.vue
│   │       ├── tsconfig.app.json
│   │       ├── tsconfig.json
│   │       ├── tsconfig.node.json
│   │       └── vite.config.ts
│   ├── frontend-sdk/
│   │   ├── .dockerignore
│   │   ├── .eslintignore
│   │   ├── .eslintrc.cjs
│   │   ├── .gitignore
│   │   ├── .nvmrc
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── jest.config.cjs
│   │   ├── jsdoc.json
│   │   ├── nginx/
│   │   │   └── default.conf
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── Hanko.ts
│   │   │   ├── declarations.d.ts
│   │   │   ├── index.ts
│   │   │   └── lib/
│   │   │       ├── Cookie.ts
│   │   │       ├── Dto.ts
│   │   │       ├── Errors.ts
│   │   │       ├── Pkce.ts
│   │   │       ├── SessionStorage.ts
│   │   │       ├── Throttle.ts
│   │   │       ├── WebauthnSupport.ts
│   │   │       ├── client/
│   │   │       │   ├── Client.ts
│   │   │       │   ├── HttpClient.ts
│   │   │       │   ├── SessionClient.ts
│   │   │       │   └── UserClient.ts
│   │   │       ├── events/
│   │   │       │   ├── CustomEvents.ts
│   │   │       │   ├── Dispatcher.ts
│   │   │       │   ├── Listener.ts
│   │   │       │   ├── Relay.ts
│   │   │       │   ├── Scheduler.ts
│   │   │       │   ├── SessionChannel.ts
│   │   │       │   ├── SessionState.ts
│   │   │       │   └── WindowActivityManager.ts
│   │   │       └── flow-api/
│   │   │           ├── State.ts
│   │   │           ├── WebauthnManager.ts
│   │   │           ├── auto-steps.ts
│   │   │           ├── passkey-autofill-activation.ts
│   │   │           └── types/
│   │   │               ├── action.ts
│   │   │               ├── flow.ts
│   │   │               ├── flowError.ts
│   │   │               ├── input.ts
│   │   │               ├── payload.ts
│   │   │               └── state.ts
│   │   ├── tests/
│   │   │   ├── Hanko.spec.ts
│   │   │   ├── lib/
│   │   │   │   ├── Cookie.spec.ts
│   │   │   │   ├── Throttle.spec.ts
│   │   │   │   ├── WebauthnSupport.spec.ts
│   │   │   │   ├── client/
│   │   │   │   │   ├── HttpClient.spec.ts
│   │   │   │   │   └── UserClient.spec.ts
│   │   │   │   ├── events/
│   │   │   │   │   ├── Dispatcher.spec.ts
│   │   │   │   │   └── Listener.spec.ts
│   │   │   │   └── flow-api/
│   │   │   │       └── State.spec.ts
│   │   │   ├── setup.ts
│   │   │   └── types.d.ts
│   │   ├── tsconfig.json
│   │   └── tsconfig.prod.json
│   ├── package.json
│   └── turbo.json
├── quickstart/
│   ├── Dockerfile
│   ├── README.md
│   ├── go.mod
│   ├── go.sum
│   ├── main.go
│   ├── middleware/
│   │   ├── cache_control.go
│   │   └── session.go
│   └── public/
│       ├── assets/
│       │   └── css/
│       │       ├── common.css
│       │       ├── fonts.css
│       │       ├── index.css
│       │       └── secured.css
│       └── html/
│           ├── error.html
│           ├── index.html
│           ├── secured.html
│           └── unauthorized.html
└── skaffold.yaml
Download .txt
Showing preview only (297K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3009 symbols across 523 files)

FILE: backend/audit_log/logger.go
  type Logger (line 17) | type Logger interface
  type logger (line 22) | type logger struct
    method Create (line 60) | func (l *logger) Create(context echo.Context, auditLogType models.Audi...
    method CreateWithConnection (line 64) | func (l *logger) CreateWithConnection(tx *pop.Connection, context echo...
    method store (line 93) | func (l *logger) store(tx *pop.Connection, auditLog models.AuditLog) e...
    method logToConsole (line 97) | func (l *logger) logToConsole(auditLog models.AuditLog) {
    method getRequestMeta (line 125) | func (l *logger) getRequestMeta(c echo.Context) models.RequestMeta {
    method mask (line 133) | func (l *logger) mask(auditLog models.AuditLog) models.AuditLog {
  function NewLogger (line 30) | func NewLogger(persister persistence.Persister, cfg config.AuditLog) Log...
  type DetailOption (line 50) | type DetailOption
  function Detail (line 52) | func Detail(key string, value interface{}) DetailOption {

FILE: backend/build_info/build_info.go
  function GetVersion (line 15) | func GetVersion() string {
  function getIsDirty (line 26) | func getIsDirty() bool {

FILE: backend/cmd/cleanup/cleanup.go
  type options (line 16) | type options struct
  type handlerParam (line 24) | type handlerParam struct
  type handlerFunc (line 32) | type handlerFunc
  constant tableAuditLogs (line 36) | tableAuditLogs           = "audit_logs"
  constant tableFlows (line 37) | tableFlows               = "flows"
  constant tableWebauthnSessionData (line 38) | tableWebauthnSessionData = "webauthn_session_data"
  function isTableAllowed (line 72) | func isTableAllowed(table string) bool {
  function validateTables (line 82) | func validateTables(tables []string) error {
  function newCleanupCommand (line 100) | func newCleanupCommand() *cobra.Command {
  function cleanup (line 160) | func cleanup[T any](param handlerParam, persister persistence.Cleanup[T]...
  function RegisterCommands (line 200) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/isready/isready.go
  function NewIsReadyCommand (line 12) | func NewIsReadyCommand() *cobra.Command {
  function RegisterCommands (line 69) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/jwk/create.go
  function NewCreateCommand (line 13) | func NewCreateCommand() *cobra.Command {

FILE: backend/cmd/jwk/root.go
  function NewMigrateCmd (line 8) | func NewMigrateCmd() *cobra.Command {
  function RegisterCommands (line 16) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/jwt/create.go
  function NewCreateCommand (line 20) | func NewCreateCommand() *cobra.Command {

FILE: backend/cmd/jwt/root.go
  function NewJwtCmd (line 7) | func NewJwtCmd() *cobra.Command {
  function RegisterCommands (line 15) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/migrate/down.go
  function NewMigrateDownCommand (line 14) | func NewMigrateDownCommand() *cobra.Command {

FILE: backend/cmd/migrate/root.go
  function NewMigrateCmd (line 14) | func NewMigrateCmd() *cobra.Command {
  function RegisterCommands (line 22) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/migrate/up.go
  function NewMigrateUpCommand (line 11) | func NewMigrateUpCommand() *cobra.Command {

FILE: backend/cmd/root.go
  function NewRootCmd (line 21) | func NewRootCmd() *cobra.Command {
  function Execute (line 42) | func Execute() {

FILE: backend/cmd/schema/generate.go
  function NewGenerateCommand (line 14) | func NewGenerateCommand() *cobra.Command {
  type generateSchemaParams (line 27) | type generateSchemaParams struct
  function generateSchema (line 36) | func generateSchema(params generateSchemaParams) error {

FILE: backend/cmd/schema/generate_config.go
  function NewGenerateConfigCommand (line 9) | func NewGenerateConfigCommand() *cobra.Command {

FILE: backend/cmd/schema/generate_import.go
  function NewGenerateImportCommand (line 9) | func NewGenerateImportCommand() *cobra.Command {

FILE: backend/cmd/schema/markdown.go
  function NewMarkdownCommand (line 5) | func NewMarkdownCommand() *cobra.Command {

FILE: backend/cmd/schema/markdown_config.go
  function NewMarkdownConfigCommand (line 12) | func NewMarkdownConfigCommand() *cobra.Command {

FILE: backend/cmd/schema/markdown_import.go
  function NewMarkdownImportCommand (line 12) | func NewMarkdownImportCommand() *cobra.Command {

FILE: backend/cmd/schema/root.go
  function NewSchemaCommand (line 7) | func NewSchemaCommand() *cobra.Command {
  function RegisterCommands (line 15) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/serve/admin.go
  function NewServeAdminCommand (line 15) | func NewServeAdminCommand() *cobra.Command {

FILE: backend/cmd/serve/all.go
  function NewServeAllCommand (line 17) | func NewServeAllCommand() *cobra.Command {

FILE: backend/cmd/serve/public.go
  function NewServePublicCommand (line 16) | func NewServePublicCommand() *cobra.Command {

FILE: backend/cmd/serve/root.go
  function NewServeCommand (line 11) | func NewServeCommand() *cobra.Command {
  function RegisterCommands (line 19) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/siwa/siwa.go
  function NewSignInWithAppleCommand (line 17) | func NewSignInWithAppleCommand() *cobra.Command {
  function RegisterCommands (line 74) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/user/export.go
  function NewExportCommand (line 14) | func NewExportCommand() *cobra.Command {
  function export (line 50) | func export(persister persistence.Persister, outFile string) error {

FILE: backend/cmd/user/format.go
  type ImportOrExportEmail (line 13) | type ImportOrExportEmail struct
    method JSONSchemaExtend (line 22) | func (ImportOrExportEmail) JSONSchemaExtend(schema *jsonschema.Schema) {
  type Emails (line 27) | type Emails
  type ImportWebauthnCredential (line 29) | type ImportWebauthnCredential struct
  type ImportWebauthnCredentials (line 61) | type ImportWebauthnCredentials
  type ImportPasswordCredential (line 63) | type ImportPasswordCredential struct
  type ImportOTPSecret (line 72) | type ImportOTPSecret struct
  type ImportOrExportEntry (line 82) | type ImportOrExportEntry struct
    method JSONSchemaExtend (line 101) | func (ImportOrExportEntry) JSONSchemaExtend(schema *jsonschema.Schema) {
    method validate (line 145) | func (entry *ImportOrExportEntry) validate(v *validator.Validate) error {
  type ImportOrExportList (line 106) | type ImportOrExportList
    method JSONSchemaExtend (line 108) | func (ImportOrExportList) JSONSchemaExtend(schema *jsonschema.Schema) {

FILE: backend/cmd/user/format_test.go
  constant validUUID (line 13) | validUUID = "62418053-a2cd-47a8-9b61-4426380d263a"
  constant invalidUUID (line 14) | invalidUUID = "notvalid"
  function TestImportEntry_validate (line 16) | func TestImportEntry_validate(t *testing.T) {

FILE: backend/cmd/user/generate.go
  function NewGenerateCommand (line 17) | func NewGenerateCommand() *cobra.Command {
  function generate (line 39) | func generate() error {

FILE: backend/cmd/user/import.go
  function NewImportCommand (line 22) | func NewImportCommand() *cobra.Command {
  function loadAndValidate (line 107) | func loadAndValidate(input io.Reader) ([]ImportOrExportEntry, error) {
  function addToDatabase (line 157) | func addToDatabase(entries []ImportOrExportEntry, persister persistence....

FILE: backend/cmd/user/import_test.go
  constant validUUID2 (line 18) | validUUID2 = "799e95f0-4cc7-4bd7-9f01-5fdc4fa26ea3"
  function TestImportSuite (line 20) | func TestImportSuite(t *testing.T) {
  type importSuite (line 25) | type importSuite struct
    method Test_loadAndValidate (line 29) | func (s *importSuite) Test_loadAndValidate() {
    method Test_addToDatabase (line 105) | func (s *importSuite) Test_addToDatabase() {

FILE: backend/cmd/user/importer.go
  type Importer (line 12) | type Importer struct
    method createUser (line 18) | func (i *Importer) createUser(newUser ImportOrExportEntry) (*models.Us...
    method createEmailAddress (line 49) | func (i *Importer) createEmailAddress(userID uuid.UUID, newEmail Impor...
    method createPrimaryEmailAddress (line 72) | func (i *Importer) createPrimaryEmailAddress(userID uuid.UUID, emailID...
    method createUsername (line 90) | func (i *Importer) createUsername(userID uuid.UUID, username string) e...
    method createWebauthnCredential (line 108) | func (i *Importer) createWebauthnCredential(userID uuid.UUID, webauthn...
    method createPasswordCredential (line 179) | func (i *Importer) createPasswordCredential(userID uuid.UUID, password...
    method createOTPSecret (line 207) | func (i *Importer) createOTPSecret(userID uuid.UUID, otpSecret ImportO...

FILE: backend/cmd/user/root.go
  function NewUserCommand (line 7) | func NewUserCommand() *cobra.Command {
  function RegisterCommands (line 15) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/cmd/version/version.go
  function NewVersionCommand (line 9) | func NewVersionCommand() *cobra.Command {
  function RegisterCommands (line 25) | func RegisterCommands(parent *cobra.Command) {

FILE: backend/config/config.go
  type Config (line 15) | type Config struct
    method Validate (line 135) | func (c *Config) Validate() error {
    method convertLegacyConfig (line 193) | func (c *Config) convertLegacyConfig() {
    method convertLegacyServerSideSessionConfig (line 208) | func (c *Config) convertLegacyServerSideSessionConfig() {
    method PostProcess (line 218) | func (c *Config) PostProcess() error {
  function LoadFile (line 84) | func LoadFile(filePath *string, pa koanf.Parser) (*koanf.Koanf, error) {
  function Load (line 98) | func Load(cfgFile *string) (*Config, error) {

FILE: backend/config/config_account.go
  type Account (line 3) | type Account struct

FILE: backend/config/config_audit_log.go
  type AuditLog (line 8) | type AuditLog struct
    method Validate (line 21) | func (al *AuditLog) Validate() error {
  type AuditLogStorage (line 29) | type AuditLogStorage struct
  type AuditLogConsole (line 34) | type AuditLogConsole struct
  type OutputStream (line 41) | type OutputStream

FILE: backend/config/config_database.go
  type Database (line 8) | type Database struct
    method Validate (line 28) | func (d *Database) Validate() error {

FILE: backend/config/config_default.go
  function DefaultConfig (line 5) | func DefaultConfig() *Config {

FILE: backend/config/config_email.go
  type Email (line 5) | type Email struct
    method Validate (line 43) | func (e *Email) Validate() error {
    method PostProcess (line 51) | func (e *Email) PostProcess() error {
  type PasscodeCharset (line 36) | type PasscodeCharset

FILE: backend/config/config_email_delivery.go
  type EmailDelivery (line 8) | type EmailDelivery struct
  type SMTP (line 21) | type SMTP struct
    method Validate (line 28) | func (s *SMTP) Validate() error {

FILE: backend/config/config_emails.go
  type Emails (line 3) | type Emails struct

FILE: backend/config/config_flow_locker.go
  type FlowLocker (line 8) | type FlowLocker struct
    method Validate (line 27) | func (f *FlowLocker) Validate() error {
  type FlowLockerStoreType (line 20) | type FlowLockerStoreType
  constant FLOW_LOCKER_STORE_IN_MEMORY (line 23) | FLOW_LOCKER_STORE_IN_MEMORY FlowLockerStoreType = "in_memory"
  constant FLOW_LOCKER_STORE_REDIS (line 24) | FLOW_LOCKER_STORE_REDIS     FlowLockerStoreType = "redis"

FILE: backend/config/config_logger.go
  type LoggerConfig (line 3) | type LoggerConfig struct

FILE: backend/config/config_mfa.go
  type SecurityKeys (line 8) | type SecurityKeys struct
  type TOTP (line 26) | type TOTP struct
  type MFA (line 31) | type MFA struct
    method JSONSchemaExtend (line 59) | func (MFA) JSONSchemaExtend(schema *jsonschema.Schema) {

FILE: backend/config/config_passcode.go
  type Passcode (line 3) | type Passcode struct

FILE: backend/config/config_passkey.go
  type Passkey (line 5) | type Passkey struct
    method JSONSchemaExtend (line 32) | func (Passkey) JSONSchemaExtend(schema *jsonschema.Schema) {

FILE: backend/config/config_password.go
  type Password (line 5) | type Password struct
    method JSONSchemaExtend (line 27) | func (Password) JSONSchemaExtend(schema *jsonschema.Schema) {

FILE: backend/config/config_privacy.go
  type Privacy (line 3) | type Privacy struct

FILE: backend/config/config_rate_limiter.go
  type RateLimiter (line 8) | type RateLimiter struct
    method Validate (line 44) | func (r *RateLimiter) Validate() error {
  type RateLimits (line 27) | type RateLimits struct
  type RateLimiterStoreType (line 37) | type RateLimiterStoreType
  constant RATE_LIMITER_STORE_IN_MEMORY (line 40) | RATE_LIMITER_STORE_IN_MEMORY RateLimiterStoreType = "in_memory"
  constant RATE_LIMITER_STORE_REDIS (line 41) | RATE_LIMITER_STORE_REDIS                          = "redis"

FILE: backend/config/config_secrets.go
  type Secrets (line 11) | type Secrets struct
    method JSONSchemaExtend (line 27) | func (Secrets) JSONSchemaExtend(schema *jsonschema.Schema) {
    method Validate (line 65) | func (s *Secrets) Validate() error {
  type KeyManagement (line 72) | type KeyManagement struct
    method JSONSchemaExtend (line 91) | func (KeyManagement) JSONSchemaExtend(schema *jsonschema.Schema) {
    method Validate (line 109) | func (k *KeyManagement) Validate() error {
  type KeyManagementStoreType (line 124) | type KeyManagementStoreType

FILE: backend/config/config_security_notifications.go
  type SecurityNotifications (line 3) | type SecurityNotifications struct
  type SecurityNotificationTypes (line 7) | type SecurityNotificationTypes struct
  type SecurityNotificationConfiguration (line 17) | type SecurityNotificationConfiguration struct

FILE: backend/config/config_server.go
  type Server (line 9) | type Server struct
    method Validate (line 16) | func (s *Server) Validate() error {
  type ServerSettings (line 28) | type ServerSettings struct
    method Validate (line 65) | func (s *ServerSettings) Validate() error {
  type Cors (line 37) | type Cors struct
    method Validate (line 55) | func (cors *Cors) Validate() error {

FILE: backend/config/config_service.go
  type Service (line 8) | type Service struct
    method Validate (line 14) | func (s *Service) Validate() error {

FILE: backend/config/config_session.go
  type Session (line 10) | type Session struct
    method Validate (line 100) | func (s *Session) Validate() error {
  type Cookie (line 109) | type Cookie struct
    method JSONSchemaExtend (line 132) | func (Cookie) JSONSchemaExtend(schema *jsonschema.Schema) {
    method GetName (line 141) | func (c *Cookie) GetName() string {
  type ServerSide (line 149) | type ServerSide struct
  type JWTTemplate (line 159) | type JWTTemplate struct

FILE: backend/config/config_shared.go
  type RedisConfig (line 5) | type RedisConfig struct
    method JSONSchemaExtend (line 12) | func (t RedisConfig) JSONSchemaExtend(schema *jsonschema.Schema) {

FILE: backend/config/config_test.go
  function TestDefaultConfigAccountParameters (line 12) | func TestDefaultConfigAccountParameters(t *testing.T) {
  function TestDefaultConfigSmtpParameters (line 18) | func TestDefaultConfigSmtpParameters(t *testing.T) {
  function TestParseValidConfig (line 23) | func TestParseValidConfig(t *testing.T) {
  function TestRootSmtpPasscodeSmtpConflict (line 34) | func TestRootSmtpPasscodeSmtpConflict(t *testing.T) {
  function TestMinimalConfigValidates (line 40) | func TestMinimalConfigValidates(t *testing.T) {
  function TestRateLimiterConfig (line 51) | func TestRateLimiterConfig(t *testing.T) {
  function TestFlowLockerConfig (line 83) | func TestFlowLockerConfig(t *testing.T) {
  function TestEnvironmentVariables (line 115) | func TestEnvironmentVariables(t *testing.T) {
  function TestParseSecurityNotificationsConfig (line 130) | func TestParseSecurityNotificationsConfig(t *testing.T) {
  function TestParseDefaultSecurityNotificationsConfig (line 150) | func TestParseDefaultSecurityNotificationsConfig(t *testing.T) {

FILE: backend/config/config_third_party.go
  type ThirdParty (line 14) | type ThirdParty struct
    method Validate (line 59) | func (t *ThirdParty) Validate() error {
    method JSONSchemaExtend (line 104) | func (t ThirdParty) JSONSchemaExtend(schema *jsonschema.Schema) {
    method JSONSchemaNoBuiltInProviderEnabled (line 119) | func (t ThirdParty) JSONSchemaNoBuiltInProviderEnabled() *jsonschema.S...
    method JSONSchemaNoCustomProviderEnabled (line 140) | func (t ThirdParty) JSONSchemaNoCustomProviderEnabled() *jsonschema.Sc...
    method PostProcess (line 160) | func (t *ThirdParty) PostProcess() error {
  type CustomThirdPartyProviders (line 185) | type CustomThirdPartyProviders
    method GetEnabled (line 187) | func (p *CustomThirdPartyProviders) GetEnabled() []CustomThirdPartyPro...
    method HasEnabled (line 198) | func (p *CustomThirdPartyProviders) HasEnabled() bool {
    method Validate (line 208) | func (p *CustomThirdPartyProviders) Validate() error {
  type CustomThirdPartyProvider (line 222) | type CustomThirdPartyProvider struct
    method Validate (line 306) | func (p *CustomThirdPartyProvider) Validate() error {
    method JSONSchemaExtend (line 338) | func (CustomThirdPartyProvider) JSONSchemaExtend(schema *jsonschema.Sc...
  type ThirdPartyProviders (line 377) | type ThirdPartyProviders struct
    method Validate (line 394) | func (p *ThirdPartyProviders) Validate() error {
    method HasEnabled (line 406) | func (p *ThirdPartyProviders) HasEnabled() bool {
    method GetEnabled (line 418) | func (p *ThirdPartyProviders) GetEnabled() []ThirdPartyProvider {
    method Get (line 431) | func (p *ThirdPartyProviders) Get(provider string) *ThirdPartyProvider {
  type ThirdPartyProvider (line 443) | type ThirdPartyProvider struct
    method JSONSchemaExtend (line 473) | func (ThirdPartyProvider) JSONSchemaExtend(schema *jsonschema.Schema) {
    method Validate (line 488) | func (p *ThirdPartyProvider) Validate() error {

FILE: backend/config/config_username.go
  type Username (line 3) | type Username struct

FILE: backend/config/config_webauthn.go
  type WebauthnSettings (line 14) | type WebauthnSettings struct
    method Validate (line 26) | func (r *WebauthnSettings) Validate() error {
    method PostProcess (line 34) | func (r *WebauthnSettings) PostProcess() error {
  type RelyingParty (line 71) | type RelyingParty struct
  type WebauthnTimeouts (line 89) | type WebauthnTimeouts struct

FILE: backend/config/config_webhook.go
  type WebhookSettings (line 12) | type WebhookSettings struct
    method Validate (line 26) | func (ws *WebhookSettings) Validate() error {
  type Webhooks (line 39) | type Webhooks
    method Decode (line 44) | func (wd *Webhooks) Decode(value string) error {
  type Webhook (line 60) | type Webhook struct
    method JSONSchemaExtend (line 67) | func (Webhook) JSONSchemaExtend(schema *jsonschema.Schema) {
    method Validate (line 114) | func (w *Webhook) Validate() error {

FILE: backend/config/config_webhook_test.go
  function TestWebhooks_Decode (line 8) | func TestWebhooks_Decode(t *testing.T) {

FILE: backend/crypto/aes_gcm/aes_gcm.go
  type AESGCM (line 15) | type AESGCM struct
    method Encrypt (line 44) | func (a *AESGCM) Encrypt(plaintext []byte) (string, error) {
    method Decrypt (line 67) | func (a *AESGCM) Decrypt(ciphertext string) (plaintext []byte, err err...
    method decrypt (line 76) | func (a *AESGCM) decrypt(ciphertext string, key [32]byte) ([]byte, err...
  function NewAESGCM (line 20) | func NewAESGCM(keys []string) (*AESGCM, error) {
  function hashSecret (line 38) | func hashSecret(key string) (res [32]byte) {

FILE: backend/crypto/aes_gcm/aes_gcm_test.go
  function TestNewEncryptionKey (line 11) | func TestNewEncryptionKey(t *testing.T) {
  function TestNewAESGCM (line 19) | func TestNewAESGCM(t *testing.T) {
  function TestAESGCM_EncryptDecrypt (line 66) | func TestAESGCM_EncryptDecrypt(t *testing.T) {
  function TestAESGCM_Decrypt (line 81) | func TestAESGCM_Decrypt(t *testing.T) {
  function TestAESGCM_SomeoneModifiedTheCiphertext (line 93) | func TestAESGCM_SomeoneModifiedTheCiphertext(t *testing.T) {
  function newEncryptionKey (line 114) | func newEncryptionKey() *[32]byte {

FILE: backend/crypto/jwk/aws_kms/adapter.go
  type AWSKMSAdapter (line 23) | type AWSKMSAdapter struct
    method Public (line 55) | func (k *AWSKMSAdapter) Public() crypto.PublicKey {
    method Sign (line 94) | func (k *AWSKMSAdapter) Sign(rand io.Reader, digest []byte, opts crypt...
    method Verify (line 129) | func (k *AWSKMSAdapter) Verify(message, signature []byte, algorithm jw...
  function NewAWSKMSAdapter (line 31) | func NewAWSKMSAdapter(ctx context.Context, cfg config.KeyManagement) (*A...

FILE: backend/crypto/jwk/aws_kms/manager.go
  type AWSKMSManager (line 16) | type AWSKMSManager struct
    method GenerateKey (line 36) | func (m *AWSKMSManager) GenerateKey() (jwk.Key, error) {
    method GetPublicKeys (line 40) | func (m *AWSKMSManager) GetPublicKeys() (jwk.Set, error) {
    method GetSigningKey (line 67) | func (m *AWSKMSManager) GetSigningKey() (jwk.Key, error) {
    method Sign (line 71) | func (m *AWSKMSManager) Sign(token jwt.Token) ([]byte, error) {
    method Verify (line 84) | func (m *AWSKMSManager) Verify(bytes []byte) (jwt.Token, error) {
  function NewAWSKMSManager (line 21) | func NewAWSKMSManager(cfg config.KeyManagement) (*AWSKMSManager, error) {

FILE: backend/crypto/jwk/local_db/generator.go
  type KeyGenerator (line 6) | type KeyGenerator interface

FILE: backend/crypto/jwk/local_db/generator_rsa.go
  type RSAKeyGenerator (line 12) | type RSAKeyGenerator struct
    method Generate (line 15) | func (g *RSAKeyGenerator) Generate(id string) (jwk.Key, error) {

FILE: backend/crypto/jwk/local_db/generator_test.go
  function TestGenerator (line 13) | func TestGenerator(t *testing.T) {

FILE: backend/crypto/jwk/local_db/manager.go
  type DefaultManager (line 17) | type DefaultManager struct
    method GenerateKey (line 50) | func (m *DefaultManager) GenerateKey() (jwk.Key, error) {
    method GetSigningKey (line 77) | func (m *DefaultManager) GetSigningKey() (jwk.Key, error) {
    method GetPublicKeys (line 95) | func (m *DefaultManager) GetPublicKeys() (jwk.Set, error) {
    method Sign (line 128) | func (m *DefaultManager) Sign(token jwt.Token) ([]byte, error) {
    method Verify (line 141) | func (m *DefaultManager) Verify(signed []byte) (jwt.Token, error) {
  function NewDefaultManager (line 24) | func NewDefaultManager(keys []string, persister persistence.JwkPersister...

FILE: backend/crypto/jwk/local_db/manager_test.go
  function TestJWKManagerSuite (line 13) | func TestJWKManagerSuite(t *testing.T) {
  type jwkManagerSuite (line 18) | type jwkManagerSuite struct
    method TestDefaultManager (line 22) | func (s *jwkManagerSuite) TestDefaultManager() {

FILE: backend/crypto/jwk/manager.go
  function NewManager (line 12) | func NewManager(cfg config.Secrets, persister persistence.Persister) (Ke...

FILE: backend/crypto/jwk/types.go
  type KeyProvider (line 9) | type KeyProvider interface
  type Manager (line 14) | type Manager interface
  type Generator (line 23) | type Generator interface

FILE: backend/crypto/passcode.go
  type PasscodeGenerator (line 9) | type PasscodeGenerator interface
  type numericPasscodeGenerator (line 13) | type numericPasscodeGenerator struct
    method Generate (line 20) | func (g *numericPasscodeGenerator) Generate() (string, error) {
  function NewNumericPasscodeGenerator (line 16) | func NewNumericPasscodeGenerator() PasscodeGenerator {
  type alphanumericPasscodeGenerator (line 29) | type alphanumericPasscodeGenerator struct
    method Generate (line 39) | func (a *alphanumericPasscodeGenerator) Generate() (string, error) {
  constant alphanumericChars (line 33) | alphanumericChars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrst...
  function NewAlphanumericPasscodeGenerator (line 35) | func NewAlphanumericPasscodeGenerator() PasscodeGenerator {

FILE: backend/crypto/passcode_test.go
  function TestPasscodeGenerator_Generate (line 9) | func TestPasscodeGenerator_Generate(t *testing.T) {
  function TestPasscodeGenerator_Generate_Different_Codes (line 18) | func TestPasscodeGenerator_Generate_Different_Codes(t *testing.T) {
  function TestAlphanumericPasscodeGenerator_Generate (line 32) | func TestAlphanumericPasscodeGenerator_Generate(t *testing.T) {
  function TestAlphanumericPasscodeGenerator_Generate_Different_Codes (line 41) | func TestAlphanumericPasscodeGenerator_Generate_Different_Codes(t *testi...

FILE: backend/crypto/string.go
  function GenerateRandomBytes (line 12) | func GenerateRandomBytes(n int) ([]byte, error) {
  function GenerateRandomStringURLSafe (line 28) | func GenerateRandomStringURLSafe(n int) (string, error) {

FILE: backend/dto/admin/email.go
  type Email (line 9) | type Email struct
  function FromEmailModel (line 19) | func FromEmailModel(email *models.Email) *Email {
  type CreateEmail (line 30) | type CreateEmail struct
  type EmailRequests (line 36) | type EmailRequests interface
  type ListEmailRequestDto (line 40) | type ListEmailRequestDto struct
  type CreateEmailRequestDto (line 44) | type CreateEmailRequestDto struct
  type GetEmailRequestDto (line 49) | type GetEmailRequestDto struct

FILE: backend/dto/admin/identity.go
  type Identity (line 10) | type Identity struct
  function FromIdentityModel (line 19) | func FromIdentityModel(model models.Identity) Identity {

FILE: backend/dto/admin/metadata.go
  type PatchMetadataRequest (line 12) | type PatchMetadataRequest struct
    method UnmarshalJSON (line 16) | func (m *PatchMetadataRequest) UnmarshalJSON(data []byte) error {
  type Metadata (line 53) | type Metadata struct
  function NewMetadata (line 59) | func NewMetadata(metadata *models.UserMetadata) *Metadata {

FILE: backend/dto/admin/otp.go
  type GetOTPRequestDto (line 8) | type GetOTPRequestDto struct
  type OTPDto (line 12) | type OTPDto struct

FILE: backend/dto/admin/password.go
  type PasswordCredential (line 8) | type PasswordCredential struct
  type GetPasswordCredentialRequestDto (line 14) | type GetPasswordCredentialRequestDto struct
  type CreateOrUpdatePasswordCredentialRequestDto (line 18) | type CreateOrUpdatePasswordCredentialRequestDto struct

FILE: backend/dto/admin/session.go
  type CreateSessionTokenDto (line 3) | type CreateSessionTokenDto struct
  type CreateSessionTokenResponse (line 9) | type CreateSessionTokenResponse struct
  type ListSessionsRequestDto (line 13) | type ListSessionsRequestDto struct
  type DeleteSessionRequestDto (line 17) | type DeleteSessionRequestDto struct

FILE: backend/dto/admin/user.go
  type User (line 11) | type User struct
    method SetIPAddress (line 30) | func (u *User) SetIPAddress(ip string) {
    method SetUserAgent (line 34) | func (u *User) SetUserAgent(agent string) {
  function FromUserModel (line 39) | func FromUserModel(model models.User) User {
  type CreateUser (line 97) | type CreateUser struct

FILE: backend/dto/admin/username.go
  type Username (line 9) | type Username struct
  function FromUsernameModel (line 17) | func FromUsernameModel(model *models.Username) *Username {

FILE: backend/dto/admin/webauthn.go
  type ListWebauthnCredentialsRequestDto (line 3) | type ListWebauthnCredentialsRequestDto struct
  type GetWebauthnCredentialRequestDto (line 7) | type GetWebauthnCredentialRequestDto struct

FILE: backend/dto/admin/webhook.go
  type WebhookListResponseDto (line 9) | type WebhookListResponseDto struct
  type CreateWebhookRequestDto (line 14) | type CreateWebhookRequestDto struct
  type GetWebhookRequestDto (line 19) | type GetWebhookRequestDto struct
  type UpdateWebhookRequestDto (line 23) | type UpdateWebhookRequestDto struct

FILE: backend/dto/config.go
  type PublicConfig (line 10) | type PublicConfig struct
  type Password (line 18) | type Password struct
  type Emails (line 23) | type Emails struct
  type Account (line 28) | type Account struct
  function FromConfig (line 34) | func FromConfig(cfg config.Config) PublicConfig {
  function GetEnabledProviders (line 53) | func GetEnabledProviders(providers config.ThirdPartyProviders) []string {
  function UseEnterpriseConnection (line 65) | func UseEnterpriseConnection(samlConfig *samlConfig.Saml) bool {

FILE: backend/dto/email.go
  type EmailResponse (line 11) | type EmailResponse struct
  type EmailCreateRequest (line 20) | type EmailCreateRequest struct
  type EmailUpdateRequest (line 24) | type EmailUpdateRequest struct
  function FromEmailModel (line 29) | func FromEmailModel(email *models.Email, cfg *config.Config) *EmailRespo...
  type EmailJWT (line 46) | type EmailJWT struct
    method String (line 52) | func (e *EmailJWT) String() string {
  function EmailJWTFromEmailModel (line 60) | func EmailJWTFromEmailModel(email *models.Email) *EmailJWT {

FILE: backend/dto/error_handler.go
  function ToHttpError (line 9) | func ToHttpError(err error) *echo.HTTPError {
  type HTTPErrorHandlerConfig (line 26) | type HTTPErrorHandlerConfig struct
  function NewHTTPErrorHandler (line 31) | func NewHTTPErrorHandler(config HTTPErrorHandlerConfig) func(err error, ...

FILE: backend/dto/intern/WebauthnCredential.go
  function WebauthnCredentialToModel (line 13) | func WebauthnCredentialToModel(credential *webauthn.Credential, userId u...
  function WebauthnCredentialFromModel (line 49) | func WebauthnCredentialFromModel(credential *models.WebauthnCredential) ...

FILE: backend/dto/intern/WebauthnSessionData.go
  function WebauthnSessionDataFromModel (line 13) | func WebauthnSessionDataFromModel(data *models.WebauthnSessionData) *web...
  function WebauthnSessionDataToModel (line 35) | func WebauthnSessionDataToModel(data *webauthn.SessionData, operation mo...

FILE: backend/dto/intern/WebauthnUser.go
  function NewWebauthnUser (line 10) | func NewWebauthnUser(user models.User, credentials []models.WebauthnCred...
  type WebauthnUser (line 23) | type WebauthnUser struct
    method WebAuthnID (line 29) | func (u *WebauthnUser) WebAuthnID() []byte {
    method WebAuthnName (line 33) | func (u *WebauthnUser) WebAuthnName() string {
    method WebAuthnDisplayName (line 37) | func (u *WebauthnUser) WebAuthnDisplayName() string {
    method WebAuthnIcon (line 41) | func (u *WebauthnUser) WebAuthnIcon() string {
    method WebAuthnCredentials (line 45) | func (u *WebauthnUser) WebAuthnCredentials() []webauthn.Credential {

FILE: backend/dto/metadata.go
  type Metadata (line 12) | type Metadata struct
  function NewMetadata (line 18) | func NewMetadata(metadata *models.UserMetadata) *Metadata {
  type MetadataJWT (line 42) | type MetadataJWT struct
    method Public (line 78) | func (m *MetadataJWT) Public(path ...string) string {
    method Unsafe (line 86) | func (m *MetadataJWT) Unsafe(path ...string) string {
    method String (line 94) | func (m *MetadataJWT) String() string {
    method MarshalJSON (line 102) | func (m *MetadataJWT) MarshalJSON() ([]byte, error) {
  function NewMetadataJWT (line 49) | func NewMetadataJWT(public, unsafe json.RawMessage) *MetadataJWT {
  function MetadataJWTFromUserModel (line 57) | func MetadataJWTFromUserModel(metadata *models.UserMetadata) *MetadataJWT {

FILE: backend/dto/passcode.go
  type PasscodeFinishRequest (line 5) | type PasscodeFinishRequest struct
  type PasscodeInitRequest (line 10) | type PasscodeInitRequest struct
  type PasscodeReturn (line 15) | type PasscodeReturn struct

FILE: backend/dto/profile.go
  type MFAConfig (line 11) | type MFAConfig struct
  type ProfileData (line 17) | type ProfileData struct
  function ProfileDataFromUserModel (line 37) | func ProfileDataFromUserModel(user *models.User, cfg *config.Config) *Pr...

FILE: backend/dto/session.go
  type SessionData (line 14) | type SessionData struct
  function FromSessionModel (line 25) | func FromSessionModel(model models.Session, current bool) SessionData {
  type Claims (line 50) | type Claims struct
    method MarshalJSON (line 63) | func (c Claims) MarshalJSON() ([]byte, error) {
  function GetClaimsFromToken (line 96) | func GetClaimsFromToken(token jwt.Token) (*Claims, error) {
  type ValidateSessionResponse (line 174) | type ValidateSessionResponse struct
  type ValidateSessionRequest (line 183) | type ValidateSessionRequest struct

FILE: backend/dto/session_test.go
  function TestGetClaimsFromToken (line 13) | func TestGetClaimsFromToken(t *testing.T) {
  function TestClaims_MarshalJSON (line 134) | func TestClaims_MarshalJSON(t *testing.T) {
  function stringPtr (line 258) | func stringPtr(s string) *string {

FILE: backend/dto/thirdparty.go
  type ThirdPartyAuthCallback (line 12) | type ThirdPartyAuthCallback struct
    method HasError (line 19) | func (cb ThirdPartyAuthCallback) HasError() bool {
  type ThirdPartyAuthRequest (line 23) | type ThirdPartyAuthRequest struct
  type Identity (line 28) | type Identity struct
  type Identities (line 34) | type Identities
  function FromIdentitiesModel (line 36) | func FromIdentitiesModel(identities models.Identities, cfg *config.Confi...
  function FromIdentityModel (line 45) | func FromIdentityModel(identity *models.Identity, cfg *config.Config) *I...
  function getProviderDisplayName (line 57) | func getProviderDisplayName(identity *models.Identity, cfg *config.Confi...

FILE: backend/dto/user.go
  type CreateUserResponse (line 11) | type CreateUserResponse struct
  type GetUserResponse (line 17) | type GetUserResponse struct
  type UserInfoResponse (line 28) | type UserInfoResponse struct
  type UserJWT (line 36) | type UserJWT struct
    method String (line 47) | func (u *UserJWT) String() string {
  function UserJWTFromUserModel (line 56) | func UserJWTFromUserModel(userModel *models.User) UserJWT {

FILE: backend/dto/username.go
  type Username (line 9) | type Username struct
  function FromUsernameModel (line 16) | func FromUsernameModel(u *models.Username) *Username {

FILE: backend/dto/validator.go
  type CustomValidator (line 13) | type CustomValidator struct
    method Validate (line 39) | func (cv *CustomValidator) Validate(i interface{}) error {
  type ValidationErrors (line 17) | type ValidationErrors struct
  function NewCustomValidator (line 21) | func NewCustomValidator() *CustomValidator {
  function webhookEventValidator (line 48) | func webhookEventValidator(fl validator.FieldLevel) bool {
  function TransformValidationErrors (line 52) | func TransformValidationErrors(err error) []string {

FILE: backend/dto/webauthn.go
  type WebauthnCredentialUpdateRequest (line 9) | type WebauthnCredentialUpdateRequest struct
  type WebauthnCredentialResponse (line 13) | type WebauthnCredentialResponse struct
  function FromWebauthnCredentialModel (line 28) | func FromWebauthnCredentialModel(c *models.WebauthnCredential) *Webauthn...

FILE: backend/dto/webhook/email.go
  type EmailSend (line 3) | type EmailSend struct
  type PasscodeData (line 16) | type PasscodeData struct
  type SecurityNotificationData (line 23) | type SecurityNotificationData struct

FILE: backend/ee/saml/config/saml.go
  type Saml (line 11) | type Saml struct
    method GetProviderByDomain (line 45) | func (s Saml) GetProviderByDomain(domain string) *IdentityProvider {
    method PostProcess (line 110) | func (s *Saml) PostProcess() error {
    method Validate (line 126) | func (s *Saml) Validate() error {
    method ValidateEmpty (line 164) | func (s *Saml) ValidateEmpty() error {
    method ValidateUrls (line 180) | func (s *Saml) ValidateUrls() error {
  type Options (line 55) | type Options struct
  type IdentityProvider (line 71) | type IdentityProvider struct
    method Validate (line 201) | func (idp *IdentityProvider) Validate() error {
  type AttributeMap (line 89) | type AttributeMap struct
  constant invalidUrlFormat (line 177) | invalidUrlFormat = "'%s' is not a valid url"

FILE: backend/ee/saml/config/saml_test.go
  function TestSamlConfig_PostProcess (line 9) | func TestSamlConfig_PostProcess(t *testing.T) {
  function TestSamlConfig_PostProcessWithBrokenGlob (line 27) | func TestSamlConfig_PostProcessWithBrokenGlob(t *testing.T) {
  function TestSamlConfig_PostProcessEndpointTrimme (line 41) | func TestSamlConfig_PostProcessEndpointTrimme(t *testing.T) {
  function TestSamlConfig_ValidateWithDisabledSaml (line 55) | func TestSamlConfig_ValidateWithDisabledSaml(t *testing.T) {
  function TestSamlConfig_ValidateWithEnabledSaml (line 64) | func TestSamlConfig_ValidateWithEnabledSaml(t *testing.T) {
  function TestSamlConfig_ValidateEmpty (line 84) | func TestSamlConfig_ValidateEmpty(t *testing.T) {
  function TestSamlConfig_ValidateEmptyErrorWithEmptyEndpoint (line 94) | func TestSamlConfig_ValidateEmptyErrorWithEmptyEndpoint(t *testing.T) {
  function TestSamlConfig_ValidateEmptyErrorWithSpaceEndpoint (line 103) | func TestSamlConfig_ValidateEmptyErrorWithSpaceEndpoint(t *testing.T) {
  function TestSamlConfig_ValidateEmptyErrorWithEmptyAudienceUri (line 112) | func TestSamlConfig_ValidateEmptyErrorWithEmptyAudienceUri(t *testing.T) {
  function TestSamlConfig_ValidateEmptyErrorWithSpaceAudienceUri (line 122) | func TestSamlConfig_ValidateEmptyErrorWithSpaceAudienceUri(t *testing.T) {
  function TestSamlConfig_ValidateUrls (line 132) | func TestSamlConfig_ValidateUrls(t *testing.T) {
  function TestSamlConfig_ValidateUrlsWithWrongEndpointUrl (line 143) | func TestSamlConfig_ValidateUrlsWithWrongEndpointUrl(t *testing.T) {
  function TestSamlConfig_ValidateUrlsWithWrongDefaultRedirecttUrl (line 152) | func TestSamlConfig_ValidateUrlsWithWrongDefaultRedirecttUrl(t *testing....
  function TestSamlConfig_ValidateUrlsWithWrongAllowedRedirectUrl (line 162) | func TestSamlConfig_ValidateUrlsWithWrongAllowedRedirectUrl(t *testing.T) {
  function TestSamlConfig_ValidateProvider (line 173) | func TestSamlConfig_ValidateProvider(t *testing.T) {
  function TestSamlConfig_ValidateProviderErrorWithEmnptyDomain (line 185) | func TestSamlConfig_ValidateProviderErrorWithEmnptyDomain(t *testing.T) {
  function TestSamlConfig_ValidateProviderErrorWithSpaceDomain (line 194) | func TestSamlConfig_ValidateProviderErrorWithSpaceDomain(t *testing.T) {
  function TestSamlConfig_ValidateProviderErrorWithEmptyName (line 203) | func TestSamlConfig_ValidateProviderErrorWithEmptyName(t *testing.T) {
  function TestSamlConfig_ValidateProviderErrorWithSpaceName (line 213) | func TestSamlConfig_ValidateProviderErrorWithSpaceName(t *testing.T) {
  function TestSamlConfig_ValidateProviderErrorWithInvalidDomain (line 223) | func TestSamlConfig_ValidateProviderErrorWithInvalidDomain(t *testing.T) {
  function TestSamlConfig_ValidateProviderErrorWithEmptyMetadataUrl (line 233) | func TestSamlConfig_ValidateProviderErrorWithEmptyMetadataUrl(t *testing...
  function TestSamlConfig_ValidateProviderErrorWithSpaceMetadataUrl (line 244) | func TestSamlConfig_ValidateProviderErrorWithSpaceMetadataUrl(t *testing...
  function TestSamlConfig_ValidateProviderErrorWithInvalidMetadataUrl (line 255) | func TestSamlConfig_ValidateProviderErrorWithInvalidMetadataUrl(t *testi...

FILE: backend/ee/saml/dto/saml.go
  type SamlRequest (line 3) | type SamlRequest struct
  type SamlMetadataRequest (line 7) | type SamlMetadataRequest struct
  type SamlAuthRequest (line 12) | type SamlAuthRequest struct

FILE: backend/ee/saml/handler.go
  type Handler (line 24) | type Handler struct
    method Metadata (line 38) | func (handler *Handler) Metadata(c echo.Context) error {
    method Auth (line 73) | func (handler *Handler) Auth(c echo.Context) error {
    method callbackPostIdPInitiated (line 103) | func (handler *Handler) callbackPostIdPInitiated(c echo.Context, samlR...
    method CallbackPost (line 246) | func (handler *Handler) CallbackPost(c echo.Context) error {
    method isIDPInitiated (line 311) | func (handler *Handler) isIDPInitiated(relayState string) bool {
    method linkAccount (line 315) | func (handler *Handler) linkAccount(c echo.Context, redirectTo *url.UR...
    method getAssertionInfo (line 366) | func (handler *Handler) getAssertionInfo(provider provider.ServiceProv...
    method redirectError (line 383) | func (handler *Handler) redirectError(c echo.Context, error error, to ...
    method auditError (line 395) | func (handler *Handler) auditError(c echo.Context, err error) error {
    method GetProvider (line 406) | func (handler *Handler) GetProvider(c echo.Context) error {
  function NewSamlHandler (line 30) | func NewSamlHandler(sessionManager session.Manager, auditLogger auditlog...

FILE: backend/ee/saml/provider/auth0.go
  type Auth0Provider (line 9) | type Auth0Provider struct
    method UseDefaultAttributesIfEmpty (line 27) | func (sp *Auth0Provider) UseDefaultAttributesIfEmpty() {
  function NewAuth0ServiceProvider (line 13) | func NewAuth0ServiceProvider(config *config.Config, idpConfig samlConfig...

FILE: backend/ee/saml/provider/provider.go
  type IdpMetadata (line 23) | type IdpMetadata struct
  type ServiceProvider (line 29) | type ServiceProvider interface
  function loadCertificate (line 38) | func loadCertificate(cfg *config.Config, persister persistence.SamlCerti...
  function fetchIdpMetadata (line 73) | func fetchIdpMetadata(idpConfig samlConfig.IdentityProvider) (*IdpMetada...
  function parseCertificate (line 115) | func parseCertificate(index int, x509Certificate dsigTypes.X509Certifica...
  function GetProvider (line 135) | func GetProvider(providerName string, cfg *config.Config, idpConfig saml...

FILE: backend/ee/saml/provider/saml.go
  type BaseSamlProvider (line 16) | type BaseSamlProvider struct
    method ProvideMetadataAsXml (line 58) | func (sp *BaseSamlProvider) ProvideMetadataAsXml() ([]byte, error) {
    method GetUserData (line 70) | func (sp *BaseSamlProvider) GetUserData(assertionInfo *saml2.Assertion...
    method mapCustomClaims (line 118) | func (sp *BaseSamlProvider) mapCustomClaims(values saml2.Values, attri...
    method UseDefaultAttributesIfEmpty (line 138) | func (sp *BaseSamlProvider) UseDefaultAttributesIfEmpty() {
    method GetDomain (line 157) | func (sp *BaseSamlProvider) GetDomain() string {
    method GetService (line 161) | func (sp *BaseSamlProvider) GetService() *saml2.SAMLServiceProvider {
    method GetConfig (line 165) | func (sp *BaseSamlProvider) GetConfig() samlConfig.IdentityProvider {
  function NewBaseSamlProvider (line 21) | func NewBaseSamlProvider(cfg *config.Config, idpConfig samlConfig.Identi...

FILE: backend/ee/saml/router.go
  function CreateSamlRoutes (line 9) | func CreateSamlRoutes(e *echo.Echo, sessionManager session.Manager, audi...

FILE: backend/ee/saml/service.go
  type Service (line 13) | type Service interface
  type defaultService (line 22) | type defaultService struct
    method Config (line 65) | func (s *defaultService) Config() *config.Config {
    method Persister (line 69) | func (s *defaultService) Persister() persistence.Persister {
    method Providers (line 73) | func (s *defaultService) Providers() []provider.ServiceProvider {
    method GetProviderByDomain (line 77) | func (s *defaultService) GetProviderByDomain(domain string) (provider....
    method GetProviderByIssuer (line 87) | func (s *defaultService) GetProviderByIssuer(issuer string) (provider....
    method GetAuthUrl (line 97) | func (s *defaultService) GetAuthUrl(provider provider.ServiceProvider,...
  function NewSamlService (line 28) | func NewSamlService(cfg *config.Config, persister persistence.Persister)...
  function parseProviderFromMetadataUrl (line 56) | func parseProviderFromMetadataUrl(idpUrlString string) (string, error) {

FILE: backend/ee/saml/state.go
  type State (line 16) | type State struct
  constant statePrefixServiceProviderInitiated (line 25) | statePrefixServiceProviderInitiated = "hanko_spi_"
  function GenerateStateForFlowAPI (line 27) | func GenerateStateForFlowAPI(isFlow bool) func(*State) {
  function GenerateState (line 33) | func GenerateState(config *config.Config, persister persistence.SamlStat...
  function VerifyState (line 87) | func VerifyState(config *config.Config, persister persistence.SamlStateP...
  function decodeState (line 116) | func decodeState(config *config.Config, state string) (*State, error) {

FILE: backend/ee/saml/state_test.go
  function TestSamlSuite (line 15) | func TestSamlSuite(t *testing.T) {
  type samlSuite (line 20) | type samlSuite struct
    method TestSaml_GenerateState (line 24) | func (s *samlSuite) TestSaml_GenerateState() {
    method TestSaml_GenerateStateWithDefaultRedirect (line 39) | func (s *samlSuite) TestSaml_GenerateStateWithDefaultRedirect() {
    method TestSaml_GenerateState_Error (line 54) | func (s *samlSuite) TestSaml_GenerateState_Error() {
    method TestSaml_VerifyState (line 98) | func (s *samlSuite) TestSaml_VerifyState() {
    method TestSaml_VerifyState_Error (line 118) | func (s *samlSuite) TestSaml_VerifyState_Error() {

FILE: backend/ee/saml/utils/response.go
  constant defaultMaxDecompressedResponseSize (line 14) | defaultMaxDecompressedResponseSize = 5 * 1024 * 1024
  function maybeDeflate (line 17) | func maybeDeflate(data []byte, maxSize int64, decoder func([]byte) error...
  function ParseSamlResponse (line 42) | func ParseSamlResponse(samlResponse string) (*etree.Document, *etree.Ele...
  function parseResponseXml (line 51) | func parseResponseXml(xml []byte) (*etree.Document, *etree.Element, erro...

FILE: backend/ee/saml/utils/url.go
  function IsAllowedRedirect (line 8) | func IsAllowedRedirect(config config.Saml, redirectTo string) bool {

FILE: backend/flow_api/flow/capabilities/action_send_capabilities.go
  type RegisterClientCapabilities (line 8) | type RegisterClientCapabilities struct
    method GetName (line 12) | func (a RegisterClientCapabilities) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a RegisterClientCapabilities) GetDescription() string {
    method Initialize (line 20) | func (a RegisterClientCapabilities) Initialize(c flowpilot.Initializat...
    method Execute (line 26) | func (a RegisterClientCapabilities) Execute(c flowpilot.ExecutionConte...

FILE: backend/flow_api/flow/credential_onboarding/action_continue_to_passkey.go
  type ContinueToPasskey (line 8) | type ContinueToPasskey struct
    method GetName (line 12) | func (a ContinueToPasskey) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a ContinueToPasskey) GetDescription() string {
    method Initialize (line 20) | func (a ContinueToPasskey) Initialize(_ flowpilot.InitializationContex...
    method Execute (line 22) | func (a ContinueToPasskey) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/credential_onboarding/action_continue_to_password.go
  type ContinueToPassword (line 8) | type ContinueToPassword struct
    method GetName (line 12) | func (a ContinueToPassword) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a ContinueToPassword) GetDescription() string {
    method Initialize (line 20) | func (a ContinueToPassword) Initialize(_ flowpilot.InitializationConte...
    method Execute (line 22) | func (a ContinueToPassword) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/credential_onboarding/action_register_password.go
  type RegisterPassword (line 12) | type RegisterPassword struct
    method GetName (line 16) | func (a RegisterPassword) GetName() flowpilot.ActionName {
    method GetDescription (line 20) | func (a RegisterPassword) GetDescription() string {
    method Initialize (line 24) | func (a RegisterPassword) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 34) | func (a RegisterPassword) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/credential_onboarding/action_skip_method_chooser.go
  type SkipCredentialOnboardingMethodChooser (line 8) | type SkipCredentialOnboardingMethodChooser struct
    method GetName (line 12) | func (a SkipCredentialOnboardingMethodChooser) GetName() flowpilot.Act...
    method GetDescription (line 16) | func (a SkipCredentialOnboardingMethodChooser) GetDescription() string {
    method Initialize (line 20) | func (a SkipCredentialOnboardingMethodChooser) Initialize(c flowpilot....
    method Execute (line 34) | func (a SkipCredentialOnboardingMethodChooser) Execute(c flowpilot.Exe...

FILE: backend/flow_api/flow/credential_onboarding/action_skip_passkey.go
  type SkipPasskey (line 8) | type SkipPasskey struct
    method GetName (line 12) | func (a SkipPasskey) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a SkipPasskey) GetDescription() string {
    method Initialize (line 20) | func (a SkipPasskey) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 49) | func (a SkipPasskey) Execute(c flowpilot.ExecutionContext) error {
    method acquirePassword (line 62) | func (a SkipPasskey) acquirePassword(c flowpilot.Context, acquireType ...

FILE: backend/flow_api/flow/credential_onboarding/action_skip_password.go
  type SkipPassword (line 8) | type SkipPassword struct
    method GetName (line 12) | func (a SkipPassword) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a SkipPassword) GetDescription() string {
    method Initialize (line 20) | func (a SkipPassword) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 49) | func (a SkipPassword) Execute(c flowpilot.ExecutionContext) error {
    method acquirePasskey (line 63) | func (a SkipPassword) acquirePasskey(c flowpilot.Context, acquireType ...

FILE: backend/flow_api/flow/credential_onboarding/action_webauthn_generate_creation_options.go
  type WebauthnGenerateCreationOptions (line 12) | type WebauthnGenerateCreationOptions struct
    method GetName (line 16) | func (a WebauthnGenerateCreationOptions) GetName() flowpilot.ActionName {
    method GetDescription (line 20) | func (a WebauthnGenerateCreationOptions) GetDescription() string {
    method Initialize (line 24) | func (a WebauthnGenerateCreationOptions) Initialize(c flowpilot.Initia...
    method Execute (line 30) | func (a WebauthnGenerateCreationOptions) Execute(c flowpilot.Execution...

FILE: backend/flow_api/flow/credential_onboarding/action_webauthn_verify_attestation_response.go
  type WebauthnVerifyAttestationResponse (line 8) | type WebauthnVerifyAttestationResponse struct
    method GetName (line 12) | func (a WebauthnVerifyAttestationResponse) GetName() flowpilot.ActionN...
    method GetDescription (line 16) | func (a WebauthnVerifyAttestationResponse) GetDescription() string {
    method Initialize (line 20) | func (a WebauthnVerifyAttestationResponse) Initialize(c flowpilot.Init...
    method Execute (line 28) | func (a WebauthnVerifyAttestationResponse) Execute(c flowpilot.Executi...

FILE: backend/flow_api/flow/credential_usage/action_continue_to_passcode_confirmation.go
  type ContinueToPasscodeConfirmation (line 9) | type ContinueToPasscodeConfirmation struct
    method GetName (line 13) | func (a ContinueToPasscodeConfirmation) GetName() flowpilot.ActionName {
    method GetDescription (line 17) | func (a ContinueToPasscodeConfirmation) GetDescription() string {
    method Initialize (line 21) | func (a ContinueToPasscodeConfirmation) Initialize(c flowpilot.Initial...
    method Execute (line 28) | func (a ContinueToPasscodeConfirmation) Execute(c flowpilot.ExecutionC...

FILE: backend/flow_api/flow/credential_usage/action_continue_to_passcode_confirmation_recovery.go
  type ContinueToPasscodeConfirmationRecovery (line 9) | type ContinueToPasscodeConfirmationRecovery struct
    method GetName (line 13) | func (a ContinueToPasscodeConfirmationRecovery) GetName() flowpilot.Ac...
    method GetDescription (line 17) | func (a ContinueToPasscodeConfirmationRecovery) GetDescription() string {
    method Initialize (line 21) | func (a ContinueToPasscodeConfirmationRecovery) Initialize(c flowpilot...
    method Execute (line 29) | func (a ContinueToPasscodeConfirmationRecovery) Execute(c flowpilot.Ex...

FILE: backend/flow_api/flow/credential_usage/action_continue_to_password_login.go
  type ContinueToPasswordLogin (line 8) | type ContinueToPasswordLogin struct
    method GetName (line 12) | func (a ContinueToPasswordLogin) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a ContinueToPasswordLogin) GetDescription() string {
    method Initialize (line 20) | func (a ContinueToPasswordLogin) Initialize(c flowpilot.Initialization...
    method Execute (line 27) | func (a ContinueToPasswordLogin) Execute(c flowpilot.ExecutionContext)...

FILE: backend/flow_api/flow/credential_usage/action_continue_with_login_identifier.go
  type ContinueWithLoginIdentifier (line 16) | type ContinueWithLoginIdentifier struct
    method GetName (line 20) | func (a ContinueWithLoginIdentifier) GetName() flowpilot.ActionName {
    method GetDescription (line 24) | func (a ContinueWithLoginIdentifier) GetDescription() string {
    method Initialize (line 28) | func (a ContinueWithLoginIdentifier) Initialize(c flowpilot.Initializa...
    method Execute (line 66) | func (a ContinueWithLoginIdentifier) Execute(c flowpilot.ExecutionCont...
    method analyzeIdentifierInputs (line 274) | func (a ContinueWithLoginIdentifier) analyzeIdentifierInputs(c flowpil...
    method continueToPasscodeConfirmation (line 305) | func (a ContinueWithLoginIdentifier) continueToPasscodeConfirmation(c ...

FILE: backend/flow_api/flow/credential_usage/action_password_login.go
  type PasswordLogin (line 16) | type PasswordLogin struct
    method GetName (line 20) | func (a PasswordLogin) GetName() flowpilot.ActionName {
    method GetDescription (line 24) | func (a PasswordLogin) GetDescription() string {
    method Initialize (line 28) | func (a PasswordLogin) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 38) | func (a PasswordLogin) Execute(c flowpilot.ExecutionContext) error {
    method wrongCredentialsError (line 127) | func (a PasswordLogin) wrongCredentialsError(c flowpilot.ExecutionCont...

FILE: backend/flow_api/flow/credential_usage/action_password_recovery.go
  type PasswordRecovery (line 16) | type PasswordRecovery struct
    method GetName (line 20) | func (a PasswordRecovery) GetName() flowpilot.ActionName {
    method GetDescription (line 24) | func (a PasswordRecovery) GetDescription() string {
    method Initialize (line 28) | func (a PasswordRecovery) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 42) | func (a PasswordRecovery) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/credential_usage/action_remember_me.go
  type RememberMe (line 10) | type RememberMe struct
    method GetName (line 14) | func (a RememberMe) GetName() flowpilot.ActionName {
    method GetDescription (line 18) | func (a RememberMe) GetDescription() string {
    method Initialize (line 22) | func (a RememberMe) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 32) | func (a RememberMe) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/credential_usage/action_resend_passcode.go
  type ReSendPasscode (line 18) | type ReSendPasscode struct
    method GetName (line 22) | func (a ReSendPasscode) GetName() flowpilot.ActionName {
    method GetDescription (line 26) | func (a ReSendPasscode) GetDescription() string {
    method Initialize (line 30) | func (a ReSendPasscode) Initialize(_ flowpilot.InitializationContext) {}
    method Execute (line 32) | func (a ReSendPasscode) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/credential_usage/action_verify_passcode.go
  type VerifyPasscode (line 15) | type VerifyPasscode struct
    method GetName (line 19) | func (a VerifyPasscode) GetName() flowpilot.ActionName {
    method GetDescription (line 23) | func (a VerifyPasscode) GetDescription() string {
    method Initialize (line 27) | func (a VerifyPasscode) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 31) | func (a VerifyPasscode) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/credential_usage/action_webauthn_generate_request_options.go
  type WebauthnGenerateRequestOptions (line 11) | type WebauthnGenerateRequestOptions struct
    method GetName (line 15) | func (a WebauthnGenerateRequestOptions) GetName() flowpilot.ActionName {
    method GetDescription (line 19) | func (a WebauthnGenerateRequestOptions) GetDescription() string {
    method Initialize (line 23) | func (a WebauthnGenerateRequestOptions) Initialize(c flowpilot.Initial...
    method Execute (line 32) | func (a WebauthnGenerateRequestOptions) Execute(c flowpilot.ExecutionC...

FILE: backend/flow_api/flow/credential_usage/action_webauthn_verify_assertion_response.go
  type WebauthnVerifyAssertionResponse (line 14) | type WebauthnVerifyAssertionResponse struct
    method GetName (line 18) | func (a WebauthnVerifyAssertionResponse) GetName() flowpilot.ActionName {
    method GetDescription (line 22) | func (a WebauthnVerifyAssertionResponse) GetDescription() string {
    method Initialize (line 26) | func (a WebauthnVerifyAssertionResponse) Initialize(c flowpilot.Initia...
    method Execute (line 34) | func (a WebauthnVerifyAssertionResponse) Execute(c flowpilot.Execution...

FILE: backend/flow_api/flow/credential_usage/hook_send_passcode.go
  type SendPasscode (line 19) | type SendPasscode struct
    method Execute (line 23) | func (h SendPasscode) Execute(c flowpilot.HookExecutionContext) error {

FILE: backend/flow_api/flow/device_trust/action_trust_device.go
  type TrustDevice (line 9) | type TrustDevice struct
    method GetName (line 13) | func (a TrustDevice) GetName() flowpilot.ActionName {
    method GetDescription (line 17) | func (a TrustDevice) GetDescription() string {
    method Initialize (line 21) | func (a TrustDevice) Initialize(c flowpilot.InitializationContext) {}
    method Execute (line 23) | func (a TrustDevice) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/device_trust/hook_issue_trust_device_cookie.go
  type IssueTrustDeviceCookie (line 12) | type IssueTrustDeviceCookie struct
    method Execute (line 16) | func (h IssueTrustDeviceCookie) Execute(c flowpilot.HookExecutionConte...

FILE: backend/flow_api/flow/device_trust/hook_schedule_trust_device_state.go
  type ScheduleTrustDeviceState (line 10) | type ScheduleTrustDeviceState struct
    method Execute (line 14) | func (h ScheduleTrustDeviceState) Execute(c flowpilot.HookExecutionCon...

FILE: backend/flow_api/flow/flows.go
  function NewLoginFlow (line 118) | func NewLoginFlow(debug bool) flowpilot.Flow {
  function NewRegistrationFlow (line 153) | func NewRegistrationFlow(debug bool) flowpilot.Flow {
  function NewProfileFlow (line 182) | func NewProfileFlow(debug bool) flowpilot.Flow {
  function NewTokenExchangeFlow (line 229) | func NewTokenExchangeFlow(debug bool) flowpilot.Flow {

FILE: backend/flow_api/flow/login/hook_create_email.go
  type CreateEmail (line 13) | type CreateEmail struct
    method Execute (line 17) | func (h CreateEmail) Execute(c flowpilot.HookExecutionContext) error {

FILE: backend/flow_api/flow/login/hook_schedule_onboarding_states.go
  type ScheduleOnboardingStates (line 12) | type ScheduleOnboardingStates struct
    method Execute (line 16) | func (h ScheduleOnboardingStates) Execute(c flowpilot.HookExecutionCon...
    method determineMFAUsageStates (line 47) | func (h ScheduleOnboardingStates) determineMFAUsageStates(c flowpilot....
    method determineCredentialOnboardingStates (line 95) | func (h ScheduleOnboardingStates) determineCredentialOnboardingStates(...
    method determineUserDetailOnboardingStates (line 178) | func (h ScheduleOnboardingStates) determineUserDetailOnboardingStates(...

FILE: backend/flow_api/flow/login/hook_trigger_login_webhook.go
  type TriggerLoginWebhook (line 11) | type TriggerLoginWebhook struct
    method Execute (line 15) | func (h TriggerLoginWebhook) Execute(c flowpilot.HookExecutionContext)...

FILE: backend/flow_api/flow/login/hook_webauthn_generate_request_options_cond.go
  type WebauthnGenerateRequestOptionsForConditionalUi (line 10) | type WebauthnGenerateRequestOptionsForConditionalUi struct
    method Execute (line 14) | func (a WebauthnGenerateRequestOptionsForConditionalUi) Execute(c flow...

FILE: backend/flow_api/flow/mfa_creation/action_continue_to_otp_secret_creation.go
  type ContinueToOTPSecretCreation (line 8) | type ContinueToOTPSecretCreation struct
    method GetName (line 12) | func (a ContinueToOTPSecretCreation) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a ContinueToOTPSecretCreation) GetDescription() string {
    method Initialize (line 20) | func (a ContinueToOTPSecretCreation) Initialize(c flowpilot.Initializa...
    method Execute (line 28) | func (a ContinueToOTPSecretCreation) Execute(c flowpilot.ExecutionCont...

FILE: backend/flow_api/flow/mfa_creation/action_continue_to_security_key_creation.go
  type ContinueToSecurityKeyCreation (line 8) | type ContinueToSecurityKeyCreation struct
    method GetName (line 12) | func (a ContinueToSecurityKeyCreation) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a ContinueToSecurityKeyCreation) GetDescription() string {
    method Initialize (line 20) | func (a ContinueToSecurityKeyCreation) Initialize(c flowpilot.Initiali...
    method Execute (line 38) | func (a ContinueToSecurityKeyCreation) Execute(c flowpilot.ExecutionCo...

FILE: backend/flow_api/flow/mfa_creation/action_otp_code_verify.go
  type OTPCodeVerify (line 15) | type OTPCodeVerify struct
    method GetName (line 19) | func (a OTPCodeVerify) GetName() flowpilot.ActionName {
    method GetDescription (line 23) | func (a OTPCodeVerify) GetDescription() string {
    method Initialize (line 27) | func (a OTPCodeVerify) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 37) | func (a OTPCodeVerify) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/mfa_creation/action_webauthn_generate_creation_options_for_security_keys.go
  type WebauthnGenerateCreationOptionsForSecurityKeys (line 12) | type WebauthnGenerateCreationOptionsForSecurityKeys struct
    method GetName (line 16) | func (a WebauthnGenerateCreationOptionsForSecurityKeys) GetName() flow...
    method GetDescription (line 20) | func (a WebauthnGenerateCreationOptionsForSecurityKeys) GetDescription...
    method Initialize (line 24) | func (a WebauthnGenerateCreationOptionsForSecurityKeys) Initialize(c f...
    method Execute (line 30) | func (a WebauthnGenerateCreationOptionsForSecurityKeys) Execute(c flow...

FILE: backend/flow_api/flow/mfa_creation/hook_otp_secret_generate.go
  type OTPSecretGenerate (line 14) | type OTPSecretGenerate struct
    method Execute (line 18) | func (h OTPSecretGenerate) Execute(c flowpilot.HookExecutionContext) e...

FILE: backend/flow_api/flow/mfa_creation/skip_mfa.go
  type SkipMFA (line 8) | type SkipMFA struct
    method GetName (line 12) | func (a SkipMFA) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a SkipMFA) GetDescription() string {
    method Initialize (line 20) | func (a SkipMFA) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 28) | func (a SkipMFA) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/mfa_usage/action_continue_to_login_otp.go
  type ContinueToLoginOTP (line 8) | type ContinueToLoginOTP struct
    method GetName (line 12) | func (a ContinueToLoginOTP) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a ContinueToLoginOTP) GetDescription() string {
    method Initialize (line 20) | func (a ContinueToLoginOTP) Initialize(c flowpilot.InitializationConte...
    method Execute (line 28) | func (a ContinueToLoginOTP) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/mfa_usage/action_continue_to_login_security_key.go
  type ContinueToLoginSecurityKey (line 8) | type ContinueToLoginSecurityKey struct
    method GetName (line 12) | func (a ContinueToLoginSecurityKey) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a ContinueToLoginSecurityKey) GetDescription() string {
    method Initialize (line 20) | func (a ContinueToLoginSecurityKey) Initialize(c flowpilot.Initializat...
    method Execute (line 29) | func (a ContinueToLoginSecurityKey) Execute(c flowpilot.ExecutionConte...

FILE: backend/flow_api/flow/mfa_usage/action_otp_code_validate.go
  type OTPCodeValidate (line 13) | type OTPCodeValidate struct
    method GetName (line 17) | func (a OTPCodeValidate) GetName() flowpilot.ActionName {
    method GetDescription (line 21) | func (a OTPCodeValidate) GetDescription() string {
    method Initialize (line 25) | func (a OTPCodeValidate) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 29) | func (a OTPCodeValidate) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/mfa_usage/action_webauthn_generate_request_options_security_key.go
  type WebauthnGenerateRequestOptionsSecurityKey (line 11) | type WebauthnGenerateRequestOptionsSecurityKey struct
    method GetName (line 15) | func (a WebauthnGenerateRequestOptionsSecurityKey) GetName() flowpilot...
    method GetDescription (line 19) | func (a WebauthnGenerateRequestOptionsSecurityKey) GetDescription() st...
    method Initialize (line 23) | func (a WebauthnGenerateRequestOptionsSecurityKey) Initialize(c flowpi...
    method Execute (line 29) | func (a WebauthnGenerateRequestOptionsSecurityKey) Execute(c flowpilot...

FILE: backend/flow_api/flow/profile/action_account_delete.go
  type AccountDelete (line 14) | type AccountDelete struct
    method GetName (line 18) | func (a AccountDelete) GetName() flowpilot.ActionName {
    method GetDescription (line 22) | func (a AccountDelete) GetDescription() string {
    method Initialize (line 26) | func (a AccountDelete) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 34) | func (a AccountDelete) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_connect_thirdparty_oauth_provider.go
  type ConnectThirdpartyOauthProvider (line 18) | type ConnectThirdpartyOauthProvider struct
    method GetName (line 22) | func (a ConnectThirdpartyOauthProvider) GetName() flowpilot.ActionName {
    method GetDescription (line 26) | func (a ConnectThirdpartyOauthProvider) GetDescription() string {
    method Initialize (line 30) | func (a ConnectThirdpartyOauthProvider) Initialize(c flowpilot.Initial...
    method Execute (line 89) | func (a ConnectThirdpartyOauthProvider) Execute(c flowpilot.ExecutionC...

FILE: backend/flow_api/flow/profile/action_continue_to_otp_secret_creation.go
  type ContinueToOTPSecretCreation (line 9) | type ContinueToOTPSecretCreation struct
    method GetName (line 13) | func (a ContinueToOTPSecretCreation) GetName() flowpilot.ActionName {
    method GetDescription (line 17) | func (a ContinueToOTPSecretCreation) GetDescription() string {
    method Initialize (line 21) | func (a ContinueToOTPSecretCreation) Initialize(c flowpilot.Initializa...
    method Execute (line 39) | func (a ContinueToOTPSecretCreation) Execute(c flowpilot.ExecutionCont...

FILE: backend/flow_api/flow/profile/action_continue_to_security_key_creation.go
  type ContinueToSecurityKeyCreation (line 9) | type ContinueToSecurityKeyCreation struct
    method GetName (line 13) | func (a ContinueToSecurityKeyCreation) GetName() flowpilot.ActionName {
    method GetDescription (line 17) | func (a ContinueToSecurityKeyCreation) GetDescription() string {
    method Initialize (line 21) | func (a ContinueToSecurityKeyCreation) Initialize(c flowpilot.Initiali...
    method Execute (line 49) | func (a ContinueToSecurityKeyCreation) Execute(c flowpilot.ExecutionCo...

FILE: backend/flow_api/flow/profile/action_disconnect_thirdparty_oauth_provider.go
  type DisconnectThirdpartyOauthProvider (line 14) | type DisconnectThirdpartyOauthProvider struct
    method GetName (line 18) | func (a DisconnectThirdpartyOauthProvider) GetName() flowpilot.ActionN...
    method GetDescription (line 22) | func (a DisconnectThirdpartyOauthProvider) GetDescription() string {
    method Initialize (line 26) | func (a DisconnectThirdpartyOauthProvider) Initialize(c flowpilot.Init...
    method Execute (line 45) | func (a DisconnectThirdpartyOauthProvider) Execute(c flowpilot.Executi...

FILE: backend/flow_api/flow/profile/action_email_create.go
  type EmailCreate (line 16) | type EmailCreate struct
    method GetName (line 20) | func (a EmailCreate) GetName() flowpilot.ActionName {
    method GetDescription (line 24) | func (a EmailCreate) GetDescription() string {
    method Initialize (line 28) | func (a EmailCreate) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 40) | func (a EmailCreate) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_email_delete.go
  type EmailDelete (line 18) | type EmailDelete struct
    method GetName (line 22) | func (a EmailDelete) GetName() flowpilot.ActionName {
    method GetDescription (line 26) | func (a EmailDelete) GetDescription() string {
    method Initialize (line 30) | func (a EmailDelete) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 74) | func (a EmailDelete) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_email_set_primary.go
  type EmailSetPrimary (line 17) | type EmailSetPrimary struct
    method GetName (line 21) | func (a EmailSetPrimary) GetName() flowpilot.ActionName {
    method GetDescription (line 25) | func (a EmailSetPrimary) GetDescription() string {
    method Initialize (line 29) | func (a EmailSetPrimary) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 56) | func (a EmailSetPrimary) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_email_verify.go
  type EmailVerify (line 11) | type EmailVerify struct
    method GetName (line 15) | func (a EmailVerify) GetName() flowpilot.ActionName {
    method GetDescription (line 19) | func (a EmailVerify) GetDescription() string {
    method Initialize (line 23) | func (a EmailVerify) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 45) | func (a EmailVerify) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_exchange_token.go
  type ExchangeToken (line 14) | type ExchangeToken struct
    method GetName (line 18) | func (a ExchangeToken) GetName() flowpilot.ActionName {
    method GetDescription (line 22) | func (a ExchangeToken) GetDescription() string {
    method Initialize (line 26) | func (a ExchangeToken) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 33) | func (a ExchangeToken) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_otp_secret_delete.go
  type OTPSecretDelete (line 12) | type OTPSecretDelete struct
    method GetName (line 16) | func (a OTPSecretDelete) GetName() flowpilot.ActionName {
    method GetDescription (line 20) | func (a OTPSecretDelete) GetDescription() string {
    method Initialize (line 24) | func (a OTPSecretDelete) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 44) | func (a OTPSecretDelete) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_password_create.go
  type PasswordCreate (line 11) | type PasswordCreate struct
    method GetName (line 15) | func (a PasswordCreate) GetName() flowpilot.ActionName {
    method GetDescription (line 19) | func (a PasswordCreate) GetDescription() string {
    method Initialize (line 23) | func (a PasswordCreate) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 45) | func (a PasswordCreate) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_password_delete.go
  type PasswordDelete (line 12) | type PasswordDelete struct
    method GetName (line 16) | func (a PasswordDelete) GetName() flowpilot.ActionName {
    method GetDescription (line 20) | func (a PasswordDelete) GetDescription() string {
    method Initialize (line 24) | func (a PasswordDelete) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 31) | func (a PasswordDelete) Execute(c flowpilot.ExecutionContext) error {
    method mustSuspend (line 70) | func (a PasswordDelete) mustSuspend(c flowpilot.Context) bool {

FILE: backend/flow_api/flow/profile/action_password_update.go
  type PasswordUpdate (line 15) | type PasswordUpdate struct
    method GetName (line 19) | func (a PasswordUpdate) GetName() flowpilot.ActionName {
    method GetDescription (line 23) | func (a PasswordUpdate) GetDescription() string {
    method Initialize (line 27) | func (a PasswordUpdate) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 47) | func (a PasswordUpdate) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_patch_metadata.go
  type PatchMetadata (line 18) | type PatchMetadata struct
    method GetName (line 22) | func (a PatchMetadata) GetName() flowpilot.ActionName {
    method GetDescription (line 26) | func (a PatchMetadata) GetDescription() string {
    method Initialize (line 30) | func (a PatchMetadata) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 34) | func (a PatchMetadata) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_security_key_create.go
  type SecurityKeyCreate (line 12) | type SecurityKeyCreate struct
    method GetName (line 16) | func (a SecurityKeyCreate) GetName() flowpilot.ActionName {
    method GetDescription (line 20) | func (a SecurityKeyCreate) GetDescription() string {
    method Initialize (line 24) | func (a SecurityKeyCreate) Initialize(c flowpilot.InitializationContex...
    method Execute (line 47) | func (a SecurityKeyCreate) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_security_key_delete.go
  type SecurityKeyDelete (line 12) | type SecurityKeyDelete struct
    method GetName (line 16) | func (a SecurityKeyDelete) GetName() flowpilot.ActionName {
    method GetDescription (line 20) | func (a SecurityKeyDelete) GetDescription() string {
    method Initialize (line 24) | func (a SecurityKeyDelete) Initialize(c flowpilot.InitializationContex...
    method Execute (line 52) | func (a SecurityKeyDelete) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_session_delete.go
  type SessionDelete (line 11) | type SessionDelete struct
    method GetName (line 15) | func (a SessionDelete) GetName() flowpilot.ActionName {
    method GetDescription (line 19) | func (a SessionDelete) GetDescription() string {
    method Initialize (line 23) | func (a SessionDelete) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 63) | func (a SessionDelete) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_username_create.go
  type UsernameCreate (line 14) | type UsernameCreate struct
    method GetName (line 18) | func (a UsernameCreate) GetName() flowpilot.ActionName {
    method GetDescription (line 22) | func (a UsernameCreate) GetDescription() string {
    method Initialize (line 26) | func (a UsernameCreate) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 49) | func (a UsernameCreate) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_username_delete.go
  type UsernameDelete (line 13) | type UsernameDelete struct
    method GetName (line 17) | func (a UsernameDelete) GetName() flowpilot.ActionName {
    method GetDescription (line 21) | func (a UsernameDelete) GetDescription() string {
    method Initialize (line 25) | func (a UsernameDelete) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 44) | func (a UsernameDelete) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_username_update.go
  type UsernameUpdate (line 14) | type UsernameUpdate struct
    method GetName (line 18) | func (a UsernameUpdate) GetName() flowpilot.ActionName {
    method GetDescription (line 22) | func (a UsernameUpdate) GetDescription() string {
    method Initialize (line 26) | func (a UsernameUpdate) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 49) | func (a UsernameUpdate) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/profile/action_webauthn_credential_create.go
  type WebauthnCredentialCreate (line 12) | type WebauthnCredentialCreate struct
    method GetName (line 16) | func (a WebauthnCredentialCreate) GetName() flowpilot.ActionName {
    method GetDescription (line 20) | func (a WebauthnCredentialCreate) GetDescription() string {
    method Initialize (line 24) | func (a WebauthnCredentialCreate) Initialize(c flowpilot.Initializatio...
    method Execute (line 34) | func (a WebauthnCredentialCreate) Execute(c flowpilot.ExecutionContext...

FILE: backend/flow_api/flow/profile/action_webauthn_credential_delete.go
  type WebauthnCredentialDelete (line 12) | type WebauthnCredentialDelete struct
    method GetName (line 16) | func (a WebauthnCredentialDelete) GetName() flowpilot.ActionName {
    method GetDescription (line 20) | func (a WebauthnCredentialDelete) GetDescription() string {
    method Initialize (line 24) | func (a WebauthnCredentialDelete) Initialize(c flowpilot.Initializatio...
    method Execute (line 33) | func (a WebauthnCredentialDelete) Execute(c flowpilot.ExecutionContext...
    method mustSuspend (line 73) | func (a WebauthnCredentialDelete) mustSuspend(c flowpilot.Context) bool {

FILE: backend/flow_api/flow/profile/action_webauthn_credential_rename.go
  type WebauthnCredentialRename (line 10) | type WebauthnCredentialRename struct
    method GetName (line 14) | func (a WebauthnCredentialRename) GetName() flowpilot.ActionName {
    method GetDescription (line 18) | func (a WebauthnCredentialRename) GetDescription() string {
    method Initialize (line 22) | func (a WebauthnCredentialRename) Initialize(c flowpilot.Initializatio...
    method Execute (line 45) | func (a WebauthnCredentialRename) Execute(c flowpilot.ExecutionContext...

FILE: backend/flow_api/flow/profile/action_webauthn_verify_attestation_response.go
  type WebauthnVerifyAttestationResponse (line 10) | type WebauthnVerifyAttestationResponse struct
    method GetName (line 14) | func (a WebauthnVerifyAttestationResponse) GetName() flowpilot.ActionN...
    method GetDescription (line 18) | func (a WebauthnVerifyAttestationResponse) GetDescription() string {
    method Initialize (line 22) | func (a WebauthnVerifyAttestationResponse) Initialize(c flowpilot.Init...
    method Execute (line 32) | func (a WebauthnVerifyAttestationResponse) Execute(c flowpilot.Executi...

FILE: backend/flow_api/flow/profile/hook_get_profile_data.go
  type GetProfileData (line 12) | type GetProfileData struct
    method Execute (line 16) | func (h GetProfileData) Execute(c flowpilot.HookExecutionContext) error {

FILE: backend/flow_api/flow/profile/hook_get_sessions.go
  type GetSessions (line 13) | type GetSessions struct
    method Execute (line 17) | func (h GetSessions) Execute(c flowpilot.HookExecutionContext) error {

FILE: backend/flow_api/flow/profile/hook_refresh_session_user.go
  type RefreshSessionUser (line 12) | type RefreshSessionUser struct
    method Execute (line 16) | func (h RefreshSessionUser) Execute(c flowpilot.HookExecutionContext) ...

FILE: backend/flow_api/flow/registration/action_register_login_identifier.go
  type RegisterLoginIdentifier (line 15) | type RegisterLoginIdentifier struct
    method GetName (line 19) | func (a RegisterLoginIdentifier) GetName() flowpilot.ActionName {
    method GetDescription (line 23) | func (a RegisterLoginIdentifier) GetDescription() string {
    method Initialize (line 27) | func (a RegisterLoginIdentifier) Initialize(c flowpilot.Initialization...
    method Execute (line 63) | func (a RegisterLoginIdentifier) Execute(c flowpilot.ExecutionContext)...
    method generateRegistrationStates (line 180) | func (a RegisterLoginIdentifier) generateRegistrationStates(c flowpilo...

FILE: backend/flow_api/flow/registration/hook_create_user.go
  type CreateUser (line 18) | type CreateUser struct
    method Execute (line 22) | func (h CreateUser) Execute(c flowpilot.HookExecutionContext) error {
    method createUser (line 66) | func (h CreateUser) createUser(c flowpilot.HookExecutionContext, id uu...

FILE: backend/flow_api/flow/registration/hook_determine_amr_values.go
  type DetermineAMRValues (line 8) | type DetermineAMRValues struct
    method Execute (line 12) | func (h DetermineAMRValues) Execute(c flowpilot.HookExecutionContext) ...

FILE: backend/flow_api/flow/shared/action_back.go
  type Back (line 7) | type Back struct
    method GetName (line 9) | func (a Back) GetName() flowpilot.ActionName {
    method GetDescription (line 13) | func (a Back) GetDescription() string {
    method Initialize (line 17) | func (a Back) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 23) | func (a Back) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/shared/action_exchange_token.go
  type ExchangeToken (line 13) | type ExchangeToken struct
    method GetName (line 17) | func (a ExchangeToken) GetName() flowpilot.ActionName {
    method GetDescription (line 21) | func (a ExchangeToken) GetDescription() string {
    method Initialize (line 25) | func (a ExchangeToken) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 32) | func (a ExchangeToken) Execute(c flowpilot.ExecutionContext) error {
    method determineOnboardingStates (line 139) | func (a ExchangeToken) determineOnboardingStates(c flowpilot.Execution...

FILE: backend/flow_api/flow/shared/action_skip.go
  type Skip (line 7) | type Skip struct
    method GetName (line 11) | func (a Skip) GetName() flowpilot.ActionName {
    method GetDescription (line 15) | func (a Skip) GetDescription() string {
    method Initialize (line 19) | func (a Skip) Initialize(c flowpilot.InitializationContext) {}
    method Execute (line 21) | func (a Skip) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/shared/action_thirdparty_oauth.go
  type ThirdPartyOAuth (line 17) | type ThirdPartyOAuth struct
    method GetName (line 21) | func (a ThirdPartyOAuth) GetName() flowpilot.ActionName {
    method GetDescription (line 25) | func (a ThirdPartyOAuth) GetDescription() string {
    method Initialize (line 29) | func (a ThirdPartyOAuth) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 62) | func (a ThirdPartyOAuth) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/shared/const_action_names.go
  constant ActionAccountDelete (line 6) | ActionAccountDelete                          flowpilot.ActionName = "acc...
  constant ActionBack (line 7) | ActionBack                                   flowpilot.ActionName = "back"
  constant ActionContinueToLoginOTP (line 8) | ActionContinueToLoginOTP                     flowpilot.ActionName = "con...
  constant ActionContinueToLoginSecurityKey (line 9) | ActionContinueToLoginSecurityKey             flowpilot.ActionName = "con...
  constant ActionContinueToOTPSecretCreation (line 10) | ActionContinueToOTPSecretCreation            flowpilot.ActionName = "con...
  constant ActionContinueToPasscodeConfirmation (line 11) | ActionContinueToPasscodeConfirmation         flowpilot.ActionName = "con...
  constant ActionContinueToPasscodeConfirmationRecovery (line 12) | ActionContinueToPasscodeConfirmationRecovery flowpilot.ActionName = "con...
  constant ActionContinueToPasskeyRegistration (line 13) | ActionContinueToPasskeyRegistration          flowpilot.ActionName = "con...
  constant ActionContinueToPasswordLogin (line 14) | ActionContinueToPasswordLogin                flowpilot.ActionName = "con...
  constant ActionContinueToPasswordRegistration (line 15) | ActionContinueToPasswordRegistration         flowpilot.ActionName = "con...
  constant ActionContinueToSecurityKeyCreation (line 16) | ActionContinueToSecurityKeyCreation          flowpilot.ActionName = "con...
  constant ActionContinueWithLoginIdentifier (line 17) | ActionContinueWithLoginIdentifier            flowpilot.ActionName = "con...
  constant ActionEmailAddressSet (line 18) | ActionEmailAddressSet                        flowpilot.ActionName = "ema...
  constant ActionEmailCreate (line 19) | ActionEmailCreate                            flowpilot.ActionName = "ema...
  constant ActionEmailDelete (line 20) | ActionEmailDelete                            flowpilot.ActionName = "ema...
  constant ActionEmailSetPrimary (line 21) | ActionEmailSetPrimary                        flowpilot.ActionName = "ema...
  constant ActionEmailVerify (line 22) | ActionEmailVerify                            flowpilot.ActionName = "ema...
  constant ActionExchangeToken (line 23) | ActionExchangeToken                          flowpilot.ActionName = "exc...
  constant ActionOTPCodeValidate (line 24) | ActionOTPCodeValidate                        flowpilot.ActionName = "otp...
  constant ActionOTPCodeVerify (line 25) | ActionOTPCodeVerify                          flowpilot.ActionName = "otp...
  constant ActionOTPSecretDelete (line 26) | ActionOTPSecretDelete                        flowpilot.ActionName = "otp...
  constant ActionPasswordCreate (line 27) | ActionPasswordCreate                         flowpilot.ActionName = "pas...
  constant ActionPasswordDelete (line 28) | ActionPasswordDelete                         flowpilot.ActionName = "pas...
  constant ActionPasswordLogin (line 29) | ActionPasswordLogin                          flowpilot.ActionName = "pas...
  constant ActionPasswordRecovery (line 30) | ActionPasswordRecovery                       flowpilot.ActionName = "pas...
  constant ActionPasswordUpdate (line 31) | ActionPasswordUpdate                         flowpilot.ActionName = "pas...
  constant ActionPatchMetadata (line 32) | ActionPatchMetadata                          flowpilot.ActionName = "pat...
  constant ActionRegisterClientCapabilities (line 33) | ActionRegisterClientCapabilities             flowpilot.ActionName = "reg...
  constant ActionRegisterLoginIdentifier (line 34) | ActionRegisterLoginIdentifier                flowpilot.ActionName = "reg...
  constant ActionRegisterPassword (line 35) | ActionRegisterPassword                       flowpilot.ActionName = "reg...
  constant ActionRememberMe (line 36) | ActionRememberMe                             flowpilot.ActionName = "rem...
  constant ActionResendPasscode (line 37) | ActionResendPasscode                         flowpilot.ActionName = "res...
  constant ActionSecurityKeyCreate (line 38) | ActionSecurityKeyCreate                      flowpilot.ActionName = "sec...
  constant ActionSecurityKeyDelete (line 39) | ActionSecurityKeyDelete                      flowpilot.ActionName = "sec...
  constant ActionSkip (line 40) | ActionSkip                                   flowpilot.ActionName = "skip"
  constant ActionThirdPartyOAuth (line 41) | ActionThirdPartyOAuth                        flowpilot.ActionName = "thi...
  constant ActionTrustDevice (line 42) | ActionTrustDevice                            flowpilot.ActionName = "tru...
  constant ActionUsernameCreate (line 43) | ActionUsernameCreate                         flowpilot.ActionName = "use...
  constant ActionUsernameDelete (line 44) | ActionUsernameDelete                         flowpilot.ActionName = "use...
  constant ActionUsernameUpdate (line 45) | ActionUsernameUpdate                         flowpilot.ActionName = "use...
  constant ActionVerifyPasscode (line 46) | ActionVerifyPasscode                         flowpilot.ActionName = "ver...
  constant ActionWebauthnCredentialCreate (line 47) | ActionWebauthnCredentialCreate               flowpilot.ActionName = "web...
  constant ActionWebauthnCredentialDelete (line 48) | ActionWebauthnCredentialDelete               flowpilot.ActionName = "web...
  constant ActionWebauthnCredentialRename (line 49) | ActionWebauthnCredentialRename               flowpilot.ActionName = "web...
  constant ActionWebauthnGenerateCreationOptions (line 50) | ActionWebauthnGenerateCreationOptions        flowpilot.ActionName = "web...
  constant ActionWebauthnGenerateRequestOptions (line 51) | ActionWebauthnGenerateRequestOptions         flowpilot.ActionName = "web...
  constant ActionWebauthnVerifyAssertionResponse (line 52) | ActionWebauthnVerifyAssertionResponse        flowpilot.ActionName = "web...
  constant ActionWebauthnVerifyAttestationResponse (line 53) | ActionWebauthnVerifyAttestationResponse      flowpilot.ActionName = "web...
  constant ActionSessionDelete (line 54) | ActionSessionDelete                          flowpilot.ActionName = "ses...
  constant ActionConnectThirdpartyOauthProvider (line 55) | ActionConnectThirdpartyOauthProvider         flowpilot.ActionName = "con...
  constant ActionDisconnectThirdpartyOauthProvider (line 56) | ActionDisconnectThirdpartyOauthProvider      flowpilot.ActionName = "dis...

FILE: backend/flow_api/flow/shared/const_email_templates.go
  constant PasscodeTemplateLogin (line 4) | PasscodeTemplateLogin                      = "login"
  constant PasscodeTemplateRecovery (line 5) | PasscodeTemplateRecovery                   = "recovery"
  constant PasscodeTemplateEmailVerification (line 6) | PasscodeTemplateEmailVerification          = "email_verification"
  constant PasscodeTemplateEmailLoginAttempted (line 7) | PasscodeTemplateEmailLoginAttempted        = "email_login_attempted"
  constant PasscodeTemplateEmailRegistrationAttempted (line 8) | PasscodeTemplateEmailRegistrationAttempted = "email_registration_attempted"

FILE: backend/flow_api/flow/shared/const_flow_names.go
  constant FlowCapabilities (line 6) | FlowCapabilities         flowpilot.FlowName = "capabilities"
  constant FlowCredentialOnboarding (line 7) | FlowCredentialOnboarding flowpilot.FlowName = "credential_onboarding"
  constant FlowCredentialUsage (line 8) | FlowCredentialUsage      flowpilot.FlowName = "credential_usage"
  constant FlowDeviceTrust (line 9) | FlowDeviceTrust          flowpilot.FlowName = "device_trust"
  constant FlowTokenExchange (line 10) | FlowTokenExchange        flowpilot.FlowName = "token_exchange"
  constant FlowLogin (line 11) | FlowLogin                flowpilot.FlowName = "login"
  constant FlowMFACreation (line 12) | FlowMFACreation          flowpilot.FlowName = "mfa_creation"
  constant FlowProfile (line 13) | FlowProfile              flowpilot.FlowName = "profile"
  constant FlowRegistration (line 14) | FlowRegistration         flowpilot.FlowName = "registration"
  constant FlowUserDetails (line 15) | FlowUserDetails          flowpilot.FlowName = "user_details"
  constant FlowMFAUsage (line 16) | FlowMFAUsage             flowpilot.FlowName = "mfa_usage"

FILE: backend/flow_api/flow/shared/const_stash_paths.go
  constant StashPathAMRValues (line 4) | StashPathAMRValues = "amr"
  constant StashPathRegistrationAMRUsedPasscode (line 7) | StashPathRegistrationAMRUsedPasscode           = "registration.amr.passc...
  constant StashPathRegistrationAMRUsedThirdParty (line 8) | StashPathRegistrationAMRUsedThirdParty         = "registration.amr.used....
  constant StashPathRegistrationAMRUsedThirdPartyProvider (line 9) | StashPathRegistrationAMRUsedThirdPartyProvider = "registration.amr.used....
  constant StashPathRegistrationAMREnrolledPwd (line 10) | StashPathRegistrationAMREnrolledPwd            = "registration.amr.enrol...
  constant StashPathRegistrationAMREnrolledPasskey (line 11) | StashPathRegistrationAMREnrolledPasskey        = "registration.amr.enrol...
  constant StashPathRegistrationAMREnrolledTotp (line 12) | StashPathRegistrationAMREnrolledTotp           = "registration.amr.enrol...
  constant StashPathRegistrationAMREnrolledSecurityKey (line 13) | StashPathRegistrationAMREnrolledSecurityKey    = "registration.amr.enrol...
  constant StashPathDeviceTrustGranted (line 15) | StashPathDeviceTrustGranted                     = "device_trust_granted"
  constant StashPathEmail (line 16) | StashPathEmail                                  = "email"
  constant StashPathEmailVerified (line 17) | StashPathEmailVerified                          = "email_verified"
  constant StashPathLoginMethod (line 18) | StashPathLoginMethod                            = "login_method"
  constant StashPathLoginOnboardingCreateEmail (line 19) | StashPathLoginOnboardingCreateEmail             = "login_onboarding_crea...
  constant StashPathLoginOnboardingScheduled (line 20) | StashPathLoginOnboardingScheduled               = "login_onboarding_sche...
  constant StashPathMFAUsageMethod (line 21) | StashPathMFAUsageMethod                         = "mfa_method"
  constant StashPathCreateMFAOnlyCredential (line 22) | StashPathCreateMFAOnlyCredential                = "create_mfa_only_crede...
  constant StashPathNewPassword (line 23) | StashPathNewPassword                            = "new_password"
  constant StashPathOTPSecret (line 24) | StashPathOTPSecret                              = "otp_secret"
  constant StashPathOTPImageSource (line 25) | StashPathOTPImageSource                         = "otp_image_src"
  constant StashPathPasscodeEmail (line 26) | StashPathPasscodeEmail                          = "sticky.passcode_email"
  constant StashPathPasscodeID (line 27) | StashPathPasscodeID                             = "sticky.passcode_id"
  constant StashPathPasscodeTemplate (line 28) | StashPathPasscodeTemplate                       = "passcode_template"
  constant StashPathPasswordRecoveryPending (line 29) | StashPathPasswordRecoveryPending                = "pw_recovery_pending"
  constant StashPathRememberMeSelected (line 30) | StashPathRememberMeSelected                     = "remember_me_selected"
  constant StashPathSecurityKeyAttachmentSupported (line 31) | StashPathSecurityKeyAttachmentSupported         = "security_key_attachme...
  constant StashPathSkipUserCreation (line 32) | StashPathSkipUserCreation                       = "skip_user_creation"
  constant StashPathThirdPartyProvider (line 33) | StashPathThirdPartyProvider                     = "third_party_provider"
  constant StashPathUserHasEmails (line 34) | StashPathUserHasEmails                          = "user_has_emails"
  constant StashPathUserHasOTPSecret (line 35) | StashPathUserHasOTPSecret                       = "user_hat_otp_secret"
  constant StashPathUserHasPasskey (line 36) | StashPathUserHasPasskey                         = "user_has_passkey"
  constant StashPathUserHasPassword (line 37) | StashPathUserHasPassword                        = "user_has_password"
  constant StashPathUserHasSecurityKey (line 38) | StashPathUserHasSecurityKey                     = "user_has_security_key"
  constant StashPathUserHasUsername (line 39) | StashPathUserHasUsername                        = "user_has_username"
  constant StashPathUserHasWebauthnCredential (line 40) | StashPathUserHasWebauthnCredential              = "user_has_webauthn_cre...
  constant StashPathUserID (line 41) | StashPathUserID                                 = "user_id"
  constant StashPathUserIdentification (line 42) | StashPathUserIdentification                     = "user_identification"
  constant StashPathUsername (line 43) | StashPathUsername                               = "username"
  constant StashPathWebauthnAvailable (line 44) | StashPathWebauthnAvailable                      = "webauthn_available"
  constant StashPathWebauthnConditionalMediationAvailable (line 45) | StashPathWebauthnConditionalMediationAvailable  = "webauthn_conditional_...
  constant StashPathWebauthnCredentials (line 46) | StashPathWebauthnCredentials                    = "webauthn_credentials"
  constant StashPathWebauthnPlatformAuthenticatorAvailable (line 47) | StashPathWebauthnPlatformAuthenticatorAvailable = "webauthn_platform_aut...
  constant StashPathWebauthnSessionDataID (line 48) | StashPathWebauthnSessionDataID                  = "webauthn_session_data...

FILE: backend/flow_api/flow/shared/const_state_names.go
  constant StateCredentialOnboardingChooser (line 6) | StateCredentialOnboardingChooser           flowpilot.StateName = "creden...
  constant StateDeviceTrust (line 7) | StateDeviceTrust                           flowpilot.StateName = "device...
  constant StateError (line 8) | StateError                                 flowpilot.StateName = "error"
  constant StateLoginInit (line 9) | StateLoginInit                             flowpilot.StateName = "login_...
  constant StateLoginMethodChooser (line 10) | StateLoginMethodChooser                    flowpilot.StateName = "login_...
  constant StateLoginOTP (line 11) | StateLoginOTP                              flowpilot.StateName = "login_...
  constant StateLoginPasskey (line 12) | StateLoginPasskey                          flowpilot.StateName = "login_...
  constant StateLoginPassword (line 13) | StateLoginPassword                         flowpilot.StateName = "login_...
  constant StateLoginPasswordRecovery (line 14) | StateLoginPasswordRecovery                 flowpilot.StateName = "login_...
  constant StateLoginSecurityKey (line 15) | StateLoginSecurityKey                      flowpilot.StateName = "login_...
  constant StateMFAMethodChooser (line 16) | StateMFAMethodChooser                      flowpilot.StateName = "mfa_me...
  constant StateOnboardingCreatePasskey (line 17) | StateOnboardingCreatePasskey               flowpilot.StateName = "onboar...
  constant StateOnboardingEmail (line 18) | StateOnboardingEmail                       flowpilot.StateName = "onboar...
  constant StateOnboardingUsername (line 19) | StateOnboardingUsername                    flowpilot.StateName = "onboar...
  constant StateOnboardingVerifyPasskeyAttestation (line 20) | StateOnboardingVerifyPasskeyAttestation    flowpilot.StateName = "onboar...
  constant StateMFAOTPSecretCreation (line 21) | StateMFAOTPSecretCreation                  flowpilot.StateName = "mfa_ot...
  constant StatePasscodeConfirmation (line 22) | StatePasscodeConfirmation                  flowpilot.StateName = "passco...
  constant StatePasswordCreation (line 23) | StatePasswordCreation                      flowpilot.StateName = "passwo...
  constant StatePreflight (line 24) | StatePreflight                             flowpilot.StateName = "prefli...
  constant StateProfileAccountDeleted (line 25) | StateProfileAccountDeleted                 flowpilot.StateName = "accoun...
  constant StateProfileInit (line 26) | StateProfileInit                           flowpilot.StateName = "profil...
  constant StateProfileWebauthnCredentialVerification (line 27) | StateProfileWebauthnCredentialVerification flowpilot.StateName = "webaut...
  constant StateRegistrationInit (line 28) | StateRegistrationInit                      flowpilot.StateName = "regist...
  constant StateMFASecurityKeyCreation (line 29) | StateMFASecurityKeyCreation                flowpilot.StateName = "mfa_se...
  constant StateSuccess (line 30) | StateSuccess                               flowpilot.StateName = "success"
  constant StateThirdParty (line 31) | StateThirdParty                            flowpilot.StateName = "thirdp...

FILE: backend/flow_api/flow/shared/flow.go
  type Dependencies (line 17) | type Dependencies struct
  type Action (line 36) | type Action struct
    method GetDeps (line 38) | func (a *Action) GetDeps(c flowpilot.Context) *Dependencies {

FILE: backend/flow_api/flow/shared/hook_determine_amr_values.go
  type DetermineAMRValues (line 7) | type DetermineAMRValues struct
    method Execute (line 11) | func (h DetermineAMRValues) Execute(c flowpilot.HookExecutionContext) ...

FILE: backend/flow_api/flow/shared/hook_email_persist_verified_status.go
  type EmailPersistVerifiedStatus (line 17) | type EmailPersistVerifiedStatus struct
    method Execute (line 21) | func (h EmailPersistVerifiedStatus) Execute(c flowpilot.HookExecutionC...

FILE: backend/flow_api/flow/shared/hook_generate_oauth_links.go
  type GenerateOAuthLinks (line 11) | type GenerateOAuthLinks struct
    method Execute (line 15) | func (h GenerateOAuthLinks) Execute(c flowpilot.HookExecutionContext) ...
    method generateHref (line 49) | func (h GenerateOAuthLinks) generateHref(c echo.Context, provider stri...

FILE: backend/flow_api/flow/shared/hook_get_user_data.go
  type GetUserData (line 10) | type GetUserData struct
    method Execute (line 14) | func (h GetUserData) Execute(c flowpilot.HookExecutionContext) error {

FILE: backend/flow_api/flow/shared/hook_issue_session.go
  type IssueSession (line 17) | type IssueSession struct
    method Execute (line 21) | func (h IssueSession) Execute(c flowpilot.HookExecutionContext) error {

FILE: backend/flow_api/flow/shared/hook_password_save.go
  type PasswordSave (line 11) | type PasswordSave struct
    method Execute (line 15) | func (h PasswordSave) Execute(c flowpilot.HookExecutionContext) error {

FILE: backend/flow_api/flow/shared/hook_persist_webauthn_credential.go
  type WebauthnCredentialSave (line 14) | type WebauthnCredentialSave struct
    method Execute (line 18) | func (h WebauthnCredentialSave) Execute(c flowpilot.HookExecutionConte...

FILE: backend/flow_api/flow/shared/hook_schedule_mfa_creation_states.go
  type ScheduleMFACreationStates (line 7) | type ScheduleMFACreationStates struct
    method Execute (line 11) | func (h ScheduleMFACreationStates) Execute(c flowpilot.HookExecutionCo...

FILE: backend/flow_api/flow/shared/hook_verify_attestation_response.go
  type VerifyAttestationResponse (line 12) | type VerifyAttestationResponse struct
    method Execute (line 16) | func (h VerifyAttestationResponse) Execute(c flowpilot.HookExecutionCo...

FILE: backend/flow_api/flow/shared/links.go
  constant CategoryLegal (line 7) | CategoryLegal flowpilot.LinkCategory = "legal"
  constant CategoryOauth (line 8) | CategoryOauth flowpilot.LinkCategory = "oauth"
  constant CategoryOther (line 9) | CategoryOther flowpilot.LinkCategory = "other"
  function LegalLink (line 13) | func LegalLink(name string, href string) flowpilot.Link {
  function OAuthLink (line 18) | func OAuthLink(name string, href string) flowpilot.Link {
  function OtherLink (line 23) | func OtherLink(name string, href string) flowpilot.Link {

FILE: backend/flow_api/flow/user_details/action_set_email.go
  type EmailAddressSet (line 10) | type EmailAddressSet struct
    method GetName (line 14) | func (a EmailAddressSet) GetName() flowpilot.ActionName {
    method GetDescription (line 18) | func (a EmailAddressSet) GetDescription() string {
    method Initialize (line 22) | func (a EmailAddressSet) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 33) | func (a EmailAddressSet) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/user_details/action_set_username.go
  type UsernameSet (line 14) | type UsernameSet struct
    method GetName (line 18) | func (a UsernameSet) GetName() flowpilot.ActionName {
    method GetDescription (line 22) | func (a UsernameSet) GetDescription() string {
    method Initialize (line 26) | func (a UsernameSet) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 37) | func (a UsernameSet) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/user_details/action_skip_email.go
  type SkipEmail (line 8) | type SkipEmail struct
    method GetName (line 12) | func (a SkipEmail) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a SkipEmail) GetDescription() string {
    method Initialize (line 20) | func (a SkipEmail) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 28) | func (a SkipEmail) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow/user_details/action_skip_username.go
  type SkipUsername (line 8) | type SkipUsername struct
    method GetName (line 12) | func (a SkipUsername) GetName() flowpilot.ActionName {
    method GetDescription (line 16) | func (a SkipUsername) GetDescription() string {
    method Initialize (line 20) | func (a SkipUsername) Initialize(c flowpilot.InitializationContext) {
    method Execute (line 27) | func (a SkipUsername) Execute(c flowpilot.ExecutionContext) error {

FILE: backend/flow_api/flow_locker/flow_locker.go
  type FlowLocker (line 12) | type FlowLocker interface
  function NewFlowLocker (line 19) | func NewFlowLocker(cfg config.FlowLocker) (FlowLocker, error) {

FILE: backend/flow_api/flow_locker/flow_locker_test.go
  function TestNewFlowLocker (line 12) | func TestNewFlowLocker(t *testing.T) {

FILE: backend/flow_api/flow_locker/memory.go
  type MemoryLocker (line 12) | type MemoryLocker struct
    method Lock (line 26) | func (m *MemoryLocker) Lock(ctx context.Context, flowID uuid.UUID) (fu...
  function NewMemoryLocker (line 18) | func NewMemoryLocker() *MemoryLocker {

FILE: backend/flow_api/flow_locker/memory_test.go
  function TestMemoryLocker_Lock_Success (line 14) | func TestMemoryLocker_Lock_Success(t *testing.T) {
  function TestMemoryLocker_Lock_FailFast_WhenAlreadyLocked (line 29) | func TestMemoryLocker_Lock_FailFast_WhenAlreadyLocked(t *testing.T) {
  function TestMemoryLocker_Lock_SucceedsAfterUnlock (line 51) | func TestMemoryLocker_Lock_SucceedsAfterUnlock(t *testing.T) {
  function TestMemoryLocker_Lock_DifferentFlowIDs (line 70) | func TestMemoryLocker_Lock_DifferentFlowIDs(t *testing.T) {
  function TestMemoryLocker_Unlock_CanBeCalled_Multiple (line 92) | func TestMemoryLocker_Unlock_CanBeCalled_Multiple(t *testing.T) {
  function TestMemoryLocker_ConcurrentLockAttempts (line 111) | func TestMemoryLocker_ConcurrentLockAttempts(t *testing.T) {
  function TestMemoryLocker_ConcurrentDifferentFlows (line 154) | func TestMemoryLocker_ConcurrentDifferentFlows(t *testing.T) {
  function TestMemoryLocker_MemoryLeak_LocksAreCleanedUp (line 195) | func TestMemoryLocker_MemoryLeak_LocksAreCleanedUp(t *testing.T) {
  function TestMemoryLocker_RaceCondition (line 216) | func TestMemoryLocker_RaceCondition(t *testing.T) {
  function TestMemoryLocker_ContextCancellation_DoesNotAffectLocking (line 260) | func TestMemoryLocker_ContextCancellation_DoesNotAffectLocking(t *testin...
  function TestMemoryLocker_SimulateRealWorldScenario (line 285) | func TestMemoryLocker_SimulateRealWorldScenario(t *testing.T) {

FILE: backend/flow_api/flow_locker/noop.go
  type NoOpLocker (line 10) | type NoOpLocker struct
    method Lock (line 18) | func (n *NoOpLocker) Lock(ctx context.Context, flowID uuid.UUID) (func...
  function NewNoOpLocker (line 13) | func NewNoOpLocker() *NoOpLocker {

FILE: backend/flow_api/flow_locker/redis.go
  type RedisLocker (line 15) | type RedisLocker struct
    method Lock (line 53) | func (r *RedisLocker) Lock(ctx context.Context, flowID uuid.UUID) (fun...
  type RedisLockerConfig (line 21) | type RedisLockerConfig struct
  function NewRedisLocker (line 28) | func NewRedisLocker(config RedisLockerConfig) *RedisLocker {

FILE: backend/flow_api/flow_locker/redis_test.go
  type RedisLockerTestSuite (line 18) | type RedisLockerTestSuite struct
    method SetupSuite (line 25) | func (suite *RedisLockerTestSuite) SetupSuite() {
    method TearDownSuite (line 67) | func (suite *RedisLockerTestSuite) TearDownSuite() {
    method getTestRedisLocker (line 74) | func (suite *RedisLockerTestSuite) getTestRedisLocker() *RedisLocker {
    method TestLock_Success (line 86) | func (suite *RedisLockerTestSuite) TestLock_Success() {
    method TestLock_FailFast_WhenAlreadyLocked (line 100) | func (suite *RedisLockerTestSuite) TestLock_FailFast_WhenAlreadyLocked...
    method TestLock_SucceedsAfterUnlock (line 119) | func (suite *RedisLockerTestSuite) TestLock_SucceedsAfterUnlock() {
    method TestLock_DifferentFlowIDs (line 138) | func (suite *RedisLockerTestSuite) TestLock_DifferentFlowIDs() {
    method TestUnlock_ReturnsError_OnMultipleCalls (line 160) | func (suite *RedisLockerTestSuite) TestUnlock_ReturnsError_OnMultipleC...
    method TestConcurrentLockAttempts (line 179) | func (suite *RedisLockerTestSuite) TestConcurrentLockAttempts() {
    method TestConcurrentDifferentFlows (line 219) | func (suite *RedisLockerTestSuite) TestConcurrentDifferentFlows() {
    method TestLockExpiry (line 257) | func (suite *RedisLockerTestSuite) TestLockExpiry() {
    method TestContextCancellation (line 281) | func (suite *RedisLockerTestSuite) TestContextCancellation() {
    method TestContextTimeout (line 293) | func (suite *RedisLockerTestSuite) TestContextTimeout() {
    method TestSimulateRealWorldScenario (line 312) | func (suite *RedisLockerTestSuite) TestSimulateRealWorldScenario() {
    method TestMultipleInstances (line 357) | func (suite *RedisLockerTestSuite) TestMultipleInstances() {
    method TestRaceCondition (line 379) | func (suite *RedisLockerTestSuite) TestRaceCondition() {
    method TestRedisLocker_UnlockReturnsError (line 420) | func (suite *RedisLockerTestSuite) TestRedisLocker_UnlockReturnsError() {
  function TestRedisLockerSuite (line 82) | func TestRedisLockerSuite(t *testing.T) {

FILE: backend/flow_api/handler.go
  type FlowPilotHandler (line 32) | type FlowPilotHandler struct
    method RegistrationFlowHandler (line 50) | func (h *FlowPilotHandler) RegistrationFlowHandler(c echo.Context) err...
    method LoginFlowHandler (line 55) | func (h *FlowPilotHandler) LoginFlowHandler(c echo.Context) error {
    method ProfileFlowHandler (line 61) | func (h *FlowPilotHandler) ProfileFlowHandler(c echo.Context) error {
    method TokenExchangeFlowHandler (line 72) | func (h *FlowPilotHandler) TokenExchangeFlowHandler(c echo.Context) er...
    method validateSession (line 77) | func (h *FlowPilotHandler) validateSession(c echo.Context) error {
    method executeFlow (line 142) | func (h *FlowPilotHandler) executeFlow(c echo.Context, flow flowpilot....
    method logFlowResult (line 229) | func (h *FlowPilotHandler) logFlowResult(c echo.Context, flowResult fl...
  function init (line 246) | func init() {
  function extractFlowID (line 251) | func extractFlowID(queryParamValue string) (uuid.UUID, error) {

FILE: backend/flow_api/services/device_trust.go
  type DeviceTrustEntry (line 18) | type DeviceTrustEntry struct
  constant entrySeparator (line 25) | entrySeparator = "|"
  constant fieldSeparator (line 27) | fieldSeparator = ":"
  type DeviceTrustService (line 30) | type DeviceTrustService struct
    method CreateTrustedDevice (line 36) | func (s DeviceTrustService) CreateTrustedDevice(userID uuid.UUID, devi...
    method CheckDeviceTrust (line 59) | func (s DeviceTrustService) CheckDeviceTrust(userID uuid.UUID) bool {
    method GenerateRandomToken (line 100) | func (s DeviceTrustService) GenerateRandomToken(length int) (string, e...
    method ParseDeviceTrustCookie (line 112) | func (s DeviceTrustService) ParseDeviceTrustCookie(cookieValue string)...
    method SerializeDeviceTrustCookie (line 147) | func (s DeviceTrustService) SerializeDeviceTrustCookie(entries []Devic...

FILE: backend/flow_api/services/email.go
  type Email (line 10) | type Email struct
    method SendEmail (line 34) | func (s *Email) SendEmail(emailAddress, subject, body, htmlBody string...
    method RenderSubject (line 50) | func (s *Email) RenderSubject(lang, template string, data map[string]i...
    method RenderBodyPlain (line 57) | func (s *Email) RenderBodyPlain(lang, template string, data map[string...
    method RenderBodyHTML (line 61) | func (s *Email) RenderBodyHTML(lang, template string, data map[string]...
  function NewEmailService (line 16) | func NewEmailService(cfg config.Config) (*Email, error) {

FILE: backend/flow_api/services/passcode.go
  type SendPasscodeParams (line 26) | type SendPasscodeParams struct
  type ValidatePasscodeParams (line 32) | type ValidatePasscodeParams struct
  type SendPasscodeResult (line 37) | type SendPasscodeResult struct
  type Passcode (line 45) | type Passcode interface
  type passcode (line 51) | type passcode struct
    method ValidatePasscode (line 72) | func (s *passcode) ValidatePasscode(p ValidatePasscodeParams) (bool, e...
    method VerifyPasscodeCode (line 89) | func (s *passcode) VerifyPasscodeCode(tx *pop.Connection, passcodeID u...
    method SendPasscode (line 120) | func (s *passcode) SendPasscode(tx *pop.Connection, p SendPasscodePara...
    method getPasscode (line 192) | func (s *passcode) getPasscode(tx *pop.Connection, passcodeID uuid.UUI...
  function NewPasscodeService (line 58) | func NewPasscodeService(cfg config.Config, emailService Email, persister...

FILE: backend/flow_api/services/password.go
  type Password (line 18) | type Password interface
  type password (line 25) | type password struct
    method VerifyPassword (line 35) | func (s password) VerifyPassword(tx *pop.Connection, userId uuid.UUID,...
    method RecoverPassword (line 61) | func (s password) RecoverPassword(tx *pop.Connection, userId uuid.UUID...
    method CreatePassword (line 82) | func (s password) CreatePassword(tx *pop.Connection, userId uuid.UUID,...
    method UpdatePassword (line 98) | func (s password) UpdatePassword(tx *pop.Connection, passwordCredentia...
  function NewPasswordService (line 29) | func NewPasswordService(persister persistence.Persister) Password {

FILE: backend/flow_api/services/security_notification.go
  type SendSecurityNotificationParams (line 19) | type SendSecurityNotificationParams struct
  type SecurityNotification (line 29) | type SecurityNotification interface
  type securityNotification (line 33) | type securityNotification struct
    method SendNotification (line 49) | func (s securityNotification) SendNotification(tx *pop.Connection, p S...
  function NewSecurityNotificationService (line 40) | func NewSecurityNotificationService(cfg config.Config, emailService Emai...

FILE: backend/flow_api/services/user.go
  function UserCanDoThirdParty (line 9) | func UserCanDoThirdParty(cfg config.Config, identities models.Identities...
  function UserCanDoSaml (line 19) | func UserCanDoSaml(cfg config.Config, identities models.Identities) bool {
  function ValidateUsername (line 29) | func ValidateUsername(name string) bool {

FILE: backend/flow_api/services/webauthn.go
  type GenerateRequestOptionsPasskeyParams (line 19) | type GenerateRequestOptionsPasskeyParams struct
  type GenerateRequestOptionsSecurityKeyParams (line 24) | type GenerateRequestOptionsSecurityKeyParams struct
  type VerifyAssertionResponseParams (line 29) | type VerifyAssertionResponseParams struct
  type GenerateCreationOptionsParams (line 36) | type GenerateCreationOptionsParams struct
  type VerifyAttestationResponseParams (line 43) | type VerifyAttestationResponseParams struct
  type WebauthnService (line 52) | type WebauthnService interface
  type webauthnUser (line 61) | type webauthnUser struct
    method WebAuthnID (line 67) | func (user webauthnUser) WebAuthnID() []byte {
    method WebAuthnName (line 71) | func (user webauthnUser) WebAuthnName() string {
    method WebAuthnDisplayName (line 83) | func (user webauthnUser) WebAuthnDisplayName() string {
    method WebAuthnCredentials (line 95) | func (user webauthnUser) WebAuthnCredentials() []webauthn.Credential {
    method WebAuthnIcon (line 99) | func (user webauthnUser) WebAuthnIcon() string {
  type webauthnService (line 108) | type webauthnService struct
    method generateRequestOptions (line 117) | func (s *webauthnService) generateRequestOptions(tx *pop.Connection, u...
    method GenerateRequestOptionsPasskey (line 143) | func (s *webauthnService) GenerateRequestOptionsPasskey(p GenerateRequ...
    method GenerateRequestOptionsSecurityKey (line 152) | func (s *webauthnService) GenerateRequestOptionsSecurityKey(p Generate...
    method VerifyAssertionResponse (line 177) | func (s *webauthnService) VerifyAssertionResponse(p VerifyAssertionRes...
    method generateCreationOptions (line 256) | func (s *webauthnService) generateCreationOptions(p GenerateCreationOp...
    method GenerateCreationOptionsSecurityKey (line 292) | func (s *webauthnService) GenerateCreationOptionsSecurityKey(p Generat...
    method GenerateCreationOptionsPasskey (line 313) | func (s *webauthnService) GenerateCreationOptionsPasskey(p GenerateCre...
    method VerifyAttestationResponse (line 329) | func (s *webauthnService) VerifyAttestationResponse(p VerifyAttestatio...
    method GetWebAuthnUser (line 361) | func (s *webauthnService) GetWebAuthnUser(tx *pop.Connection, credenti...
  function NewWebauthnService (line 113) | func NewWebauthnService(cfg config.Config, persister persistence.Persist...
  type webauthnUserWithCustomUserHandle (line 380) | type webauthnUserWithCustomUserHandle struct
    method WebAuthnID (line 385) | func (u *webauthnUserWithCustomUserHandle) WebAuthnID() []byte {

FILE: backend/flowpilot/action_input.go
  type actionInput (line 8) | type actionInput interface
  type readOnlyActionInput (line 12) | type readOnlyActionInput interface
  function newActionInput (line 17) | func newActionInput() actionInput {
  function newActionInputFromInputData (line 23) | func newActionInputFromInputData(data InputData) (actionInput, error) {

FILE: backend/flowpilot/builder.go
  type FlowBuilder (line 9) | type FlowBuilder interface
  type defaultFlowBuilderBase (line 26) | type defaultFlowBuilderBase struct
    method addState (line 75) | func (fb *defaultFlowBuilderBase) addState(stateName StateName, action...
    method addBeforeStateHooks (line 79) | func (fb *defaultFlowBuilderBase) addBeforeStateHooks(stateName StateN...
    method addAfterStateHooks (line 83) | func (fb *defaultFlowBuilderBase) addAfterStateHooks(stateName StateNa...
    method addAfterFlowHooks (line 87) | func (fb *defaultFlowBuilderBase) addAfterFlowHooks(flowName FlowName,...
    method addSubFlows (line 99) | func (fb *defaultFlowBuilderBase) addSubFlows(subFlows ...subFlow) {
    method addStateIfNotExists (line 103) | func (fb *defaultFlowBuilderBase) addStateIfNotExists(stateName StateN...
  type defaultFlowBuilder (line 39) | type defaultFlowBuilder struct
    method TTL (line 70) | func (fb *defaultFlowBuilder) TTL(ttl time.Duration) FlowBuilder {
    method addBeforeEachActionHooks (line 91) | func (fb *defaultFlowBuilder) addBeforeEachActionHooks(hooks ...HookAc...
    method addAfterEachActionHooks (line 95) | func (fb *defaultFlowBuilder) addAfterEachActionHooks(hooks ...HookAct...
    method scanFlowStates (line 111) | func (fb *defaultFlowBuilder) scanFlowStates(flow flowBase) error {
    method validate (line 163) | func (fb *defaultFlowBuilder) validate() error {
    method State (line 182) | func (fb *defaultFlowBuilder) State(stateName StateName, actions ...Ac...
    method BeforeState (line 187) | func (fb *defaultFlowBuilder) BeforeState(stateName StateName, hooks ....
    method AfterState (line 192) | func (fb *defaultFlowBuilder) AfterState(stateName StateName, hooks .....
    method AfterFlow (line 197) | func (fb *defaultFlowBuilder) AfterFlow(flowName FlowName, hooks ...Ho...
    method BeforeEachAction (line 202) | func (fb *defaultFlowBuilder) BeforeEachAction(hooks ...HookAction) Fl...
    method AfterEachAction (line 207) | func (fb *defaultFlowBuilder) AfterEachAction(hooks ...HookAction) Flo...
    method InitialState (line 212) | func (fb *defaultFlowBuilder) InitialState(nextStateNames ...StateName...
    method ErrorState (line 217) | func (fb *defaultFlowBuilder) ErrorState(stateName StateName) FlowBuil...
    method SubFlows (line 223) | func (fb *defaultFlowBuilder) SubFlows(subFlows ...subFlow) FlowBuilder {
    method Debug (line 229) | func (fb *defaultFlowBuilder) Debug(enabled bool) FlowBuilder {
    method Build (line 235) | func (fb *defaultFlowBuilder) Build() (Flow, error) {
    method MustBuild (line 276) | func (fb *defaultFlowBuilder) MustBuild() Flow {
  function newFlowBuilderBase (line 50) | func newFlowBuilderBase(name FlowName) defaultFlowBuilderBase {
  function NewFlow (line 63) | func NewFlow(name FlowName) FlowBuilder {

FILE: backend/flowpilot/builder_subflow.go
  type SubFlowBuilder (line 3) | type SubFlowBuilder interface
  type defaultSubFlowBuilder (line 13) | type defaultSubFlowBuilder struct
    method SubFlows (line 23) | func (sfb *defaultSubFlowBuilder) SubFlows(subFlows ...subFlow) SubFlo...
    method State (line 29) | func (sfb *defaultSubFlowBuilder) State(stateName StateName, actions ....
    method BeforeState (line 34) | func (sfb *defaultSubFlowBuilder) BeforeState(stateName StateName, hoo...
    method AfterState (line 39) | func (sfb *defaultSubFlowBuilder) AfterState(stateName StateName, hook...
    method Build (line 45) | func (sfb *defaultSubFlowBuilder) Build() (subFlow, error) {
    method MustBuild (line 58) | func (sfb *defaultSubFlowBuilder) MustBuild() subFlow {
  function NewSubFlow (line 18) | func NewSubFlow(name FlowName) SubFlowBuilder {

FILE: backend/flowpilot/context.go
  type context (line 11) | type context interface
  type flowContext (line 20) | type flowContext interface
  type actionInitializationContext (line 45) | type actionInitializationContext interface
  type actionExecutionContext (line 55) | type actionExecutionContext interface
  type actionExecutionContinuationContext (line 70) | type actionExecutionContinuationContext interface
  type actionSuspender (line 80) | type actionSuspender interface
  type Context (line 85) | type Context interface
  type InitializationContext (line 90) | type InitializationContext interface
  type ExecutionContext (line 96) | type ExecutionContext interface
  type HookExecutionContext (line 101) | type HookExecutionContext interface
  type BeforeEachActionExecutionContext (line 110) | type BeforeEachActionExecutionContext interface
  function createAndInitializeFlow (line 115) | func createAndInitializeFlow(db FlowDB, flow defaultFlow) (FlowResult, e...
  function executeFlowAction (line 174) | func executeFlowAction(db FlowDB, flow defaultFlow) (FlowResult, error) {

FILE: backend/flowpilot/context_action_exec.go
  type defaultActionExecutionContext (line 11) | type defaultActionExecutionContext struct
    method closeExecutionContext (line 24) | func (aec *defaultActionExecutionContext) closeExecutionContext() error {
    method executeBeforeStateHooks (line 76) | func (aec *defaultActionExecutionContext) executeBeforeStateHooks(next...
    method executeBeforeEachActionHooks (line 87) | func (aec *defaultActionExecutionContext) executeBeforeEachActionHooks...
    method executeAfterHooks (line 97) | func (aec *defaultActionExecutionContext) executeAfterHooks() error {
    method Input (line 135) | func (aec *defaultActionExecutionContext) Input() executionInputSchema {
    method Payload (line 140) | func (aec *defaultActionExecutionContext) Payload() payload {
    method CopyInputValuesToStash (line 145) | func (aec *defaultActionExecutionContext) CopyInputValuesToStash(input...
    method SetFlowError (line 158) | func (aec *defaultActionExecutionContext) SetFlowError(err FlowError) {
    method GetFlowError (line 162) | func (aec *defaultActionExecutionContext) GetFlowError() FlowError {
    method ValidateInputData (line 167) | func (aec *defaultActionExecutionContext) ValidateInputData() bool {
    method Error (line 173) | func (aec *defaultActionExecutionContext) Error(flowErr FlowError) err...
    method Revert (line 196) | func (aec *defaultActionExecutionContext) Revert() error {
    method Continue (line 212) | func (aec *defaultActionExecutionContext) Continue(stateNames ...State...
    method AddLink (line 240) | func (aec *defaultActionExecutionContext) AddLink(links ...Link) {
    method ScheduleStates (line 244) | func (aec *defaultActionExecutionContext) ScheduleStates(stateNames .....
    method Set (line 248) | func (aec *defaultActionExecutionContext) Set(key string, value interf...
    method SuspendAction (line 252) | func (aec *defaultActionExecutionContext) SuspendAction() {
    method PreventRevert (line 256) | func (aec *defaultActionExecutionContext) PreventRevert() {
    method ExecuteHook (line 260) | func (aec *defaultActionExecutionContext) ExecuteHook(a HookAction) er...

FILE: backend/flowpilot/context_action_init.go
  type defaultActionInitializationContext (line 4) | type defaultActionInitializationContext struct
    method Payload (line 10) | func (aic *defaultActionInitializationContext) Payload() payload {
    method Set (line 14) | func (aic *defaultActionInitializationContext) Set(s string, i interfa...
    method AddInputs (line 19) | func (aic *defaultActionInitializationContext) AddInputs(inputs ...Inp...
    method SuspendAction (line 24) | func (aic *defaultActionInitializationContext) SuspendAction() {
    method Stash (line 29) | func (aic *defaultActionInitializationContext) Stash() stash {
    method Get (line 34) | func (aic *defaultActionInitializationContext) Get(key string) interfa...
    method StateIsRevertible (line 38) | func (aic *defaultActionInitializationContext) StateIsRevertible() bool {

FILE: backend/flowpilot/context_flow.go
  type defaultFlowContext (line 8) | type defaultFlowContext struct
    method GetFlowID (line 17) | func (fc *defaultFlowContext) GetFlowID() uuid.UUID {
    method GetInitialState (line 22) | func (fc *defaultFlowContext) GetInitialState() StateName {
    method GetCurrentState (line 27) | func (fc *defaultFlowContext) GetCurrentState() StateName {
    method GetScheduledStates (line 31) | func (fc *defaultFlowContext) GetScheduledStates() []StateName {
    method CurrentStateEquals (line 36) | func (fc *defaultFlowContext) CurrentStateEquals(stateNames ...StateNa...
    method IsStateScheduled (line 46) | func (fc *defaultFlowContext) IsStateScheduled(name StateName) bool {
    method StateVisited (line 55) | func (fc *defaultFlowContext) StateVisited(name StateName) bool {
    method GetPreviousState (line 60) | func (fc *defaultFlowContext) GetPreviousState() StateName {
    method IsPreviousState (line 65) | func (fc *defaultFlowContext) IsPreviousState(name StateName) bool {
    method GetErrorState (line 70) | func (fc *defaultFlowContext) GetErrorState() StateName {
    method Stash (line 75) | func (fc *defaultFlowContext) Stash() stash {
    method Get (line 80) | func (fc *defaultFlowContext) Get(name string) interface{} {
    method GetFlowName (line 85) | func (fc *defaultFlowContext) GetFlowName() FlowName {
    method IsFlow (line 90) | func (fc *defaultFlowContext) IsFlow(name FlowName) bool {

FILE: backend/flowpilot/db.go
  type FlowModel (line 10) | type FlowModel struct
  type FlowDB (line 21) | type FlowDB interface
  type flowDBWrapper (line 28) | type flowDBWrapper interface
  type defaultFlowDBWrapper (line 35) | type defaultFlowDBWrapper struct
    method createFlowWithParam (line 52) | func (w *defaultFlowDBWrapper) createFlowWithParam(p flowCreationParam...
    method updateFlowWithParam (line 90) | func (w *defaultFlowDBWrapper) updateFlowWithParam(p flowUpdateParam) ...
  function wrapDB (line 40) | func wrapDB(db FlowDB) flowDBWrapper {
  type flowCreationParam (line 45) | type flowCreationParam struct
  type flowUpdateParam (line 80) | type flowUpdateParam struct

FILE: backend/flowpilot/errors.go
  type flowpilotError (line 10) | type flowpilotError interface
  type FlowError (line 21) | type FlowError interface
  type InputError (line 29) | type InputError interface
  type defaultError (line 36) | type defaultError struct
    method Code (line 44) | func (e *defaultError) Code() string {
    method Message (line 49) | func (e *defaultError) Message() string {
    method Unwrap (line 54) | func (e *defaultError) Unwrap() error {
    method Error (line 59) | func (e *defaultError) Error() string {
    method toResponseError (line 64) | func (e *defaultError) toResponseError(debug bool) *ResponseError {
  type defaultFlowError (line 82) | type defaultFlowError struct
    method Status (line 119) | func (e *defaultFlowError) Status() int {
    method Wrap (line 124) | func (e *defaultFlowError) Wrap(err error) FlowError {
  function createErrorText (line 89) | func createErrorText(code, message string, cause error) string {
  function NewFlowError (line 100) | func NewFlowError(code, message string, status int) FlowError {
  function newFlowErrorWithCause (line 105) | func newFlowErrorWithCause(code, message string, status int, cause error...
  type defaultInputError (line 129) | type defaultInputError struct
    method Wrap (line 153) | func (e *defaultInputError) Wrap(err error) InputError {
  function NewInputError (line 134) | func NewInputError(code, message string) InputError {
  function newInputErrorWithCause (line 139) | func newInputErrorWithCause(code, message string, cause error) InputError {
  function createMustBeOneOfError (line 174) | func createMustBeOneOfError(values []string) InputError {

FILE: backend/flowpilot/flow.go
  type FlowName (line 10) | type FlowName
  type InputData (line 13) | type InputData struct
  function WithQueryParamKey (line 19) | func WithQueryParamKey(key string) func(*defaultFlow) {
  function WithQueryParamValue (line 26) | func WithQueryParamValue(value string) func(*defaultFlow) {
  function WithInputData (line 33) | func WithInputData(inputData InputData) func(*defaultFlow) {
  function UseCompression (line 40) | func UseCompression(b bool) func(*defaultFlow) {
  type StateName (line 47) | type StateName
  type ActionName (line 50) | type ActionName
  type Action (line 53) | type Action interface
  type Actions (line 61) | type Actions
  type HookAction (line 64) | type HookAction interface
  type hookActions (line 69) | type hookActions
    method makeUnique (line 71) | func (actions hookActions) makeUnique() hookActions {
    method reverse (line 85) | func (actions hookActions) reverse() hookActions {
  type stateActions (line 97) | type stateActions
    method stateExists (line 118) | func (st stateActions) stateExists(stateName StateName) bool {
  type stateHooks (line 100) | type stateHooks
    method makeUnique (line 102) | func (sh stateHooks) makeUnique() {
  type flowHooks (line 109) | type flowHooks
    method makeUnique (line 111) | func (fh flowHooks) makeUnique() {
  type SubFlows (line 124) | type SubFlows
    method stateExists (line 127) | func (sfs SubFlows) stateExists(state StateName) bool {
    method getSubFlowFromStateName (line 137) | func (sfs SubFlows) getSubFlowFromStateName(state StateName) subFlow {
  type flowBase (line 147) | type flowBase interface
  type Flow (line 157) | type Flow interface
  type subFlow (line 174) | type subFlow interface
  type contextValues (line 178) | type contextValues
  type defaultFlowBase (line 180) | type defaultFlowBase struct
    method getName (line 222) | func (f *defaultFlowBase) getName() FlowName {
    method getSubFlows (line 227) | func (f *defaultFlowBase) getSubFlows() SubFlows {
    method getFlow (line 232) | func (f *defaultFlowBase) getFlow() stateActions {
    method getBeforeStateHooks (line 236) | func (f *defaultFlowBase) getBeforeStateHooks() stateHooks {
    method getAfterStateHooks (line 240) | func (f *defaultFlowBase) getAfterStateHooks() stateHooks {
    method getAfterFlowHooks (line 244) | func (f *defaultFlowBase) getAfterFlowHooks() hookActions {
  type defaultFlow (line 192) | type defaultFlow struct
    method Set (line 208) | func (f *defaultFlow) Set(name string, value interface{}) {
    method getState (line 213) | func (f *defaultFlow) getState(stateName StateName) (stateDetail, erro...
    method setDefaults (line 249) | func (f *defaultFlow) setDefaults() {
    method Execute (line 260) | func (f *defaultFlow) Execute(db FlowDB, opts ...func(flow *defaultFlo...
    method ResultFromError (line 285) | func (f *defaultFlow) ResultFromError(err error) FlowResult {

FILE: backend/flowpilot/input.go
  type inputType (line 8) | type inputType
  constant inputTypeString (line 12) | inputTypeString   inputType = "string"
  constant inputTypeBoolean (line 13) | inputTypeBoolean  inputType = "boolean"
  constant inputTypeEmail (line 14) | inputTypeEmail    inputType = "email"
  constant inputTypeNumber (line 15) | inputTypeNumber   inputType = "number"
  constant inputTypePassword (line 16) | inputTypePassword inputType = "password"
  constant inputTypeJSON (line 17) | inputTypeJSON     inputType = "json"
  type Input (line 21) | type Input interface
  type defaultExtraInputOptions (line 43) | type defaultExtraInputOptions struct
  type defaultInput (line 50) | type defaultInput struct
    method AllowedValue (line 64) | func (i *defaultInput) AllowedValue(name string, value interface{}) In...
    method MinLength (line 119) | func (i *defaultInput) MinLength(minLength int) Input {
    method MaxLength (line 125) | func (i *defaultInput) MaxLength(maxLength int) Input {
    method Required (line 131) | func (i *defaultInput) Required(b bool) Input {
    method Hidden (line 137) | func (i *defaultInput) Hidden(b bool) Input {
    method Preserve (line 144) | func (i *defaultInput) Preserve(b bool) Input {
    method TrimSpace (line 150) | func (i *defaultInput) TrimSpace(b bool) Input {
    method LowerCase (line 156) | func (i *defaultInput) LowerCase(b bool) Input {
    method setValue (line 162) | func (i *defaultInput) setValue(value interface{}) Input {
    method getName (line 168) | func (i *defaultInput) getName() string {
    method setError (line 173) | func (i *defaultInput) setError(inputError InputError) {
    method getError (line 178) | func (i *defaultInput) getError() InputError {
    method shouldPreserve (line 183) | func (i *defaultInput) shouldPreserve() bool {
    method shouldTrimSpace (line 188) | func (i *defaultInput) shouldTrimSpace() bool {
    method shouldConvertToLowerCase (line 193) | func (i *defaultInput) shouldConvertToLowerCase() bool {
    method validate (line 198) | func (i *defaultInput) validate(inputData readOnlyActionInput) bool {
    method toResponseInput (line 259) | func (i *defaultInput) toResponseInput() *ResponseInput {
  function newInput (line 73) | func newInput(name string, inputType inputType) Input {
  function StringInput (line 89) | func StringInput(name string) Input {
  function EmailInput (line 94) | func EmailInput(name string) Input {
  function NumberInput (line 99) | func NumberInput(name string) Input {
  function BooleanInput (line 104) | func BooleanInput(name string) Input {
  function PasswordInput (line 109) | func PasswordInput(name string) Input {
  function JSONInput (line 114) | func JSONInput(name string) Input {

FILE: backend/flowpilot/input_allowed_value.go
  type allowedValue (line 3) | type allowedValue interface
  type defaultAllowedValue (line 8) | type defaultAllowedValue struct
    method getValue (line 13) | func (av *defaultAllowedValue) getValue() interface{} {
    method toResponseAllowedValue (line 18) | func (av *defaultAllowedValue) toResponseAllowedValue() *ResponseAllow...
  type allowedValues (line 25) | type allowedValues interface
  type defaultAllowedValues (line 33) | type defaultAllowedValues
    method isAllowed (line 35) | func (av *defaultAllowedValues) isAllowed(value string) bool {
    method add (line 49) | func (av *defaultAllowedValues) add(value allowedValue) {
    method hasAny (line 53) | func (av *defaultAllowedValues) hasAny() bool {
    method getValues (line 57) | func (av *defaultAllowedValues) getValues() []string {
    method toResponseAllowedValues (line 65) | func (av *defaultAllowedValues) toResponseAllowedValues() *ResponseAll...

FILE: backend/flowpilot/input_schema.go
  type initializationInputSchema (line 9) | type initializationInputSchema interface
  type executionInputSchema (line 14) | type executionInputSchema interface
  type inputs (line 27) | type inputs
    method exists (line 29) | func (il *inputs) exists(input Input) bool {
  type ResponseInputs (line 39) | type ResponseInputs
  type defaultSchema (line 42) | type defaultSchema struct
    method forInitializationContext (line 64) | func (s *defaultSchema) forInitializationContext() initializationInput...
    method Get (line 69) | func (s *defaultSchema) Get(path string) gjson.Result {
    method Set (line 74) | func (s *defaultSchema) Set(path string, value interface{}) error {
    method AddInputs (line 79) | func (s *defaultSchema) AddInputs(inputList ...Input) {
    method getInput (line 95) | func (s *defaultSchema) getInput(name string) Input {
    method SetError (line 106) | func (s *defaultSchema) SetError(inputName string, inputError InputErr...
    method validateInputData (line 113) | func (s *defaultSchema) validateInputData() bool {
    method getOutputData (line 140) | func (s *defaultSchema) getOutputData() readOnlyActionInput {
    method toResponseInputs (line 145) | func (s *defaultSchema) toResponseInputs() ResponseInputs {
  function newSchemaWithInputData (line 49) | func newSchemaWithInputData(inputData actionInput) executionInputSchema {
  function newSchema (line 58) | func newSchema() executionInputSchema {

FILE: backend/flowpilot/jsonmanager/manager.go
  type ReadJSONManager (line 10) | type ReadJSONManager interface
  type JSONManager (line 17) | type JSONManager interface
  type ReadOnlyJSONManager (line 24) | type ReadOnlyJSONManager interface
  type DefaultJSONManager (line 29) | type DefaultJSONManager struct
    method Get (line 48) | func (jm *DefaultJSONManager) Get(path string) gjson.Result {
    method Set (line 53) | func (jm *DefaultJSONManager) Set(path string, value interface{}) error {
    method Delete (line 63) | func (jm *DefaultJSONManager) Delete(path string) error {
    method String (line 73) | func (jm *DefaultJSONManager) String() string {
    method Unmarshal (line 78) | func (jm *DefaultJSONManager) Unmarshal() interface{} {
  function NewJSONManager (line 34) | func NewJSONManager() JSONManager {
  function NewJSONManagerFromString (line 40) | func NewJSONManagerFromString(data string) (JSONManager, error) {

FILE: backend/flowpilot/link.go
  type LinkCategory (line 4) | type LinkCategory
  type LinkTarget (line 7) | type LinkTarget
  constant LinkTargetSelf (line 11) | LinkTargetSelf   LinkTarget = "_self"
  constant LinkTargetBlank (line 12) | LinkTargetBlank  LinkTarget = "_blank"
  constant LinkTargetParent (line 13) | LinkTargetParent LinkTarget = "_parent"
  constant LinkTargetTop (line 14) | LinkTargetTop    LinkTarget = "_top"
  type Link (line 18) | type Link interface
  type defaultLink (line 25) | type defaultLink struct
    method Target (line 33) | func (l *defaultLink) Target(target LinkTarget) Link {
    method toResponseLink (line 38) | func (l *defaultLink) toResponseLink() ResponseLink {
  function NewLink (line 48) | func NewLink(name string, category LinkCategory, href string) Link {

FILE: backend/flowpilot/payload.go
  type payload (line 5) | type payload interface
  function newPayload (line 10) | func newPayload() payload {

FILE: backend/flowpilot/query_param.go
  type queryParam (line 10) | type queryParam interface
  type parsedQueryParamValue (line 19) | type parsedQueryParamValue struct
  type defaultQueryParam (line 25) | type defaultQueryParam struct
    method getKey (line 68) | func (q *defaultQueryParam) getKey() string {
    method getValue (line 72) | func (q *defaultQueryParam) getValue() string {
    method getURLValues (line 76) | func (q *defaultQueryParam) getURLValues() url.Values {
    method getActionName (line 82) | func (q *defaultQueryParam) getActionName() ActionName {
    method getFlowID (line 86) | func (q *defaultQueryParam) getFlowID() uuid.UUID {
  function createQueryParamValue (line 31) | func createQueryParamValue(actionName ActionName, flowID uuid.UUID) stri...
  function parseQueryParamValue (line 36) | func parseQueryParamValue(value string) (*parsedQueryParamValue, error) {
  function newQueryParam (line 63) | func newQueryParam(key, value string) (queryParam, error) {

FILE: backend/flowpilot/random.go
  constant letters (line 10) | letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  function init (line 12) | func init() {
  function assertAvailablePRNG (line 16) | func assertAvailablePRNG() {
  function generateRandomString (line 26) | func generateRandomString(n int) (string, error) {

FILE: backend/flowpilot/response.go
  type ResponseAction (line 9) | type ResponseAction struct
  type ResponseActions (line 17) | type ResponseActions
  type ResponseError (line 20) | type ResponseError struct
  type ResponseAllowedValue (line 27) | type ResponseAllowedValue struct
  type ResponseAllowedValues (line 32) | type ResponseAllowedValues
  type ResponseInput (line 35) | type ResponseInput struct
  type ResponseLinks (line 48) | type ResponseLinks
  type ResponseLink (line 51) | type ResponseLink struct
  type Response (line 59) | type Response struct
  type FlowResult (line 70) | type FlowResult interface
  type defaultFlowResult (line 76) | type defaultFlowResult struct
    method GetResponse (line 101) | func (r defaultFlowResult) GetResponse() Response {
    method GetStatus (line 106) | func (r defaultFlowResult) GetStatus() int {
  function newFlowResultFromResponse (line 81) | func newFlowResultFromResponse(response Response) FlowResult {
  function newFlowResultFromError (line 86) | func newFlowResultFromError(stateName StateName, flowError FlowError, de...
  type actionExecutionResult (line 111) | type actionExecutionResult struct
  type executionResult (line 118) | type executionResult struct
    method generateResponse (line 127) | func (er *executionResult) generateResponse(fc *defaultFlowContext) Fl...
    method generateLinks (line 159) | func (er *executionResult) generateLinks() ResponseLinks {
    method generateActions (line 171) | func (er *executionResult) generateActions(fc *defaultFlowContext) Res...
    method getInputSchema (line 216) | func (er *executionResult) getInputSchema(fc *defaultFlowContext, acti...
    method createHref (line 225) | func (er *executionResult) createHref(fc *defaultFlowContext, actionNa...

FILE: backend/flowpilot/stash.go
  constant stashKeyState (line 16) | stashKeyState           = "state"
  constant stashKeyPreviousState (line 17) | stashKeyPreviousState   = "prev_state"
  constant stashKeyScheduledStates (line 18) | stashKeyScheduledStates = "scheduled"
  constant stashKeyData (line 19) | stashKeyData            = "data"
  constant stashKeyHistory (line 20) | stashKeyHistory         = "hist"
  constant stashKeyRevertible (line 21) | stashKeyRevertible      = "revertible"
  constant stashKeySticky (line 22) | stashKeySticky          = "sticky"
  type stash (line 25) | type stash interface
  type defaultStash (line 41) | type defaultStash struct
    method Get (line 152) | func (h *defaultStash) Get(path string) gjson.Result {
    method Set (line 157) | func (h *defaultStash) Set(path string, value interface{}) error {
    method Delete (line 162) | func (h *defaultStash) Delete(path string) error {
    method String (line 167) | func (h *defaultStash) String() string {
    method Unmarshal (line 176) | func (h *defaultStash) Unmarshal() interface{} {
    method pushState (line 180) | func (h *defaultStash) pushState(revertible bool) error {
    method pushErrorState (line 184) | func (h *defaultStash) pushErrorState(nextState StateName) error {
    method push (line 188) | func (h *defaultStash) push(newData string, revertible, writeHistory b...
    method stateVisited (line 245) | func (h *defaultStash) stateVisited(name StateName) bool {
    method revertState (line 257) | func (h *defaultStash) revertState() error {
    method getStateName (line 297) | func (h *defaultStash) getStateName() StateName {
    method getPreviousStateName (line 301) | func (h *defaultStash) getPreviousStateName() StateName {
    method addScheduledStateNames (line 305) | func (h *defaultStash) addScheduledStateNames(names ...StateName) {
    method getScheduledStateNames (line 308) | func (h *defaultStash) getScheduledStateNames() []StateName {
    method getNextStateName (line 317) | func (h *defaultStash) getNextStateName() StateName {
    method isRevertible (line 326) | func (h *defaultStash) isRevertible() bool {
    method useCompression (line 331) | func (h *defaultStash) useCompression(b bool) {
  function newStashFromJSONManager (line 49) | func newStashFromJSONManager(jm jsonmanager.JSONManager) stash {
  function newStash (line 60) | func newStash(nextStates ...StateName) (stash, error) {
  function newStashFromString (line 83) | func newStashFromString(data string) (stash, error) {
  function reverseStateNames (line 96) | func reverseStateNames(slice []StateName) []StateName {
  function startsWithCurlyBrace (line 104) | func startsWithCurlyBrace(s string) bool {
  function encodeData (line 113) | func encodeData(jsonData string) (string, error) {
  function decodeData (line 129) | func decodeData(base64GzippedData string) (string, error) {

FILE: backend/flowpilot/state_action.go
  type actionDetail (line 3) | type actionDetail interface
  type defaultActionDetail (line 8) | type defaultActionDetail struct
    method getAction (line 16) | func (ad *defaultActionDetail) getAction() Action {
    method getFlowName (line 20) | func (ad *defaultActionDetail) getFlowName() FlowName {
  type defaultActionDetails (line 14) | type defaultActionDetails

FILE: backend/flowpilot/state_detail.go
  type stateDetail (line 5) | type stateDetail interface
  type defaultStateDetail (line 15) | type defaultStateDetail struct
    method getName (line 23) | func (sd *defaultStateDetail) getName() StateName {
    method getFlow (line 27) | func (sd *defaultStateDetail) getFlow() stateActions {
    method getFlowName (line 31) | func (sd *defaultStateDetail) getFlowName() FlowName {
    method getSubFlows (line 35) | func (sd *defaultStateDetail) getSubFlows() SubFlows {
    method getActionDetails (line 39) | func (sd *defaultStateDetail) getActionDetails() defaultActionDetails {
    method getActionDetail (line 44) | func (sd *defaultStateDetail) getActionDetail(actionName ActionName) (...
  type stateDetails (line 57) | type stateDetails

FILE: backend/handler/admin_router.go
  function NewAdminRouter (line 19) | func NewAdminRouter(cfg *config.Config, persister persistence.Persister,...

FILE: backend/handler/audit_log.go
  type AuditLogHandler (line 15) | type AuditLogHandler struct
    method List (line 37) | func (h AuditLogHandler) List(c echo.Context) error {
  function NewAuditLogHandler (line 19) | func NewAuditLogHandler(persister persistence.Persister) *AuditLogHandler {
  type AuditLogListRequest (line 25) | type AuditLogListRequest struct

FILE: backend/handler/email.go
  type EmailHandler (line 21) | type EmailHandler struct
    method List (line 35) | func (h *EmailHandler) List(c echo.Context) error {
    method Create (line 60) | func (h *EmailHandler) Create(c echo.Context) error {
    method SetPrimaryEmail (line 157) | func (h *EmailHandler) SetPrimaryEmail(c echo.Context) error {
    method Delete (line 218) | func (h *EmailHandler) Delete(c echo.Context) error {
  function NewEmailHandler (line 27) | func NewEmailHandler(cfg *config.Config, persister persistence.Persister...

FILE: backend/handler/email_admin.go
  type EmailAdminHandler (line 19) | type EmailAdminHandler interface
  type emailAdminHandler (line 28) | type emailAdminHandler struct
    method List (line 45) | func (h *emailAdminHandler) List(ctx echo.Context) error {
    method Create (line 70) | func (h *emailAdminHandler) Create(ctx echo.Context) error {
    method Get (line 142) | func (h *emailAdminHandler) Get(ctx echo.Context) error {
    method Delete (line 175) | func (h *emailAdminHandler) Delete(ctx echo.Context) error {
    method SetPrimaryEmail (line 221) | func (h *emailAdminHandler) SetPrimaryEmail(ctx echo.Context) error {
    method makeEmailPrimary (line 267) | func (h *emailAdminHandler) makeEmailPrimary(ctx echo.Context, email *...
  constant parseUserUuidFailureMessage (line 34) | parseUserUuidFailureMessage   = "failed to parse user_id as uuid: %w"
  constant fetchUserFromDbFailureMessage (line 35) | fetchUserFromDbFailureMessage = "failed to fetch user from db: %w"
  function NewEmailAdminHandler (line 38) | func NewEmailAdminHandler(cfg *config.Config, persister persistence.Pers...

FILE: backend/handler/email_admin_test.go
  function TestEmailAdminSuite (line 18) | func TestEmailAdminSuite(t *testing.T) {
  type emailAdminSuite (line 23) | type emailAdminSuite struct
    method TestEmailAdminHandler_New (line 27) | func (s *emailAdminSuite) TestEmailAdminHandler_New() {
    method TestEmailAdminHandler_List (line 32) | func (s *emailAdminSuite) TestEmailAdminHandler_List() {
    method TestEmailAdminHandler_Create (line 101) | func (s *emailAdminSuite) TestEmailAdminHandler_Create() {
    method TestEmailAdminHandler_Get (line 240) | func (s *emailAdminSuite) TestEmailAdminHandler_Get() {
    method TestEmailAdminHandler_Delete (line 325) | func (s *emailAdminSuite) TestEmailAdminHandler_Delete() {
    method TestEmailAdminHandler_SetPrimaryEmail (line 416) | func (s *emailAdminSuite) TestEmailAdminHandler_SetPrimaryEmail() {

FILE: backend/handler/email_test.go
  function TestEmailSuite (line 17) | func TestEmailSuite(t *testing.T) {
  type emailSuite (line 22) | type emailSuite struct
    method TestEmailHandler_New (line 26) | func (s *emailSuite) TestEmailHandler_New() {
    method TestEmailHandler_List (line 31) | func (s *emailSuite) TestEmailHandler_List() {
    method TestEmailHandler_Create (line 78) | func (s *emailSuite) TestEmailHandler_Create() {
    method TestEmailHandler_SetPrimaryEmail (line 212) | func (s *emailSuite) TestEmailHandler_SetPrimaryEmail() {
    method TestEmailHandler_Delete (line 248) | func (s *emailSuite) TestEmailHandler_Delete() {
    method getAuditLogRecordsCount (line 299) | func (s *emailSuite) getAuditLogRecordsCount(code string) int {

FILE: backend/handler/health.go
  type HealthHandler (line 8) | type HealthHandler struct
    method Ready (line 14) | func (handler *HealthHandler) Ready(c echo.Context) error {
    method Alive (line 18) | func (handler *HealthHandler) Alive(c echo.Context) error {
  function NewHealthHandler (line 10) | func NewHealthHandler() *HealthHandler {

FILE: backend/handler/health_test.go
  function TestHealthHandler_Ready (line 12) | func TestHealthHandler_Ready(t *testing.T) {
  function TestHealthHandler_Alive (line 24) | func TestHealthHandler_Alive(t *testing.T) {

FILE: backend/handler/helpers_test.go
  function getDefaultSessionManager (line 16) | func getDefaultSessionManager(storage persistence.Persister) session.Man...
  function generateSessionCookie (line 22) | func generateSessionCookie(storage persistence.Persister, userId uuid.UU...

FILE: backend/handler/metadata_admin.go
  type MetadataAdminHandler (line 19) | type MetadataAdminHandler struct
    method GetMetadata (line 29) | func (h *MetadataAdminHandler) GetMetadata(c echo.Context) error {
    method PatchMetadata (line 57) | func (h *MetadataAdminHandler) PatchMetadata(c echo.Context) error {
    method applyMetadataPatch (line 105) | func (h *MetadataAdminHandler) applyMetadataPatch(currentMetadataModel...
    method buildCurrentMetadataJSON (line 137) | func (h *MetadataAdminHandler) buildCurrentMetadataJSON(metadata *mode...
    method updateMetadataModel (line 164) | func (h *MetadataAdminHandler) updateMetadataModel(metadata *models.Us...
  function NewMetadataAdminHandler (line 23) | func NewMetadataAdminHandler(persister persistence.Persister) *MetadataA...

FILE: backend/handler/metadata_admin_test.go
  function TestMetadataAdminSuite (line 18) | func TestMetadataAdminSuite(t *testing.T) {
  type metadataAdminSuite (line 23) | type metadataAdminSuite struct
    method TestMetadataAdminHandler_Get (line 27) | func (s *metadataAdminSuite) TestMetadataAdminHandler_Get() {
    method TestMetadataAdminHandler_Patch_Errors (line 133) | func (s *metadataAdminSuite) TestMetadataAdminHandler_Patch_Errors() {
    method TestMetadataAdminHandler_Patch (line 225) | func (s *metadataAdminSuite) TestMetadataAdminHandler_Patch() {
  constant metadataExceedingLimit (line 532) | metadataExceedingLimit = `{

FILE: backend/handler/otp_admin.go
  type OTPAdminHandler (line 12) | type OTPAdminHandler interface
  type otpAdminHandler (line 17) | type otpAdminHandler struct
    method Get (line 25) | func (h *otpAdminHandler) Get(ctx echo.Context) error {
    method Delete (line 51) | func (h *otpAdminHandler) Delete(ctx echo.Context) error {
  function NewOTPAdminHandler (line 21) | func NewOTPAdminHandler(persister persistence.Persister) OTPAdminHandler {

FILE: backend/handler/otp_admin_test.go
  function TestOtpAdminSuite (line 15) | func TestOtpAdminSuite(t *testing.T) {
  type otpAdminSuite (line 20) | type otpAdminSuite struct
    method TestOtpAdminHandler_Get (line 24) | func (s *otpAdminSuite) TestOtpAdminHandler_Get() {
    method TestOtpAdminHandler_Delete (line 85) | func (s *otpAdminSuite) TestOtpAdminHandler_Delete() {

FILE: backend/handler/passcode.go
  type PasscodeHandler (line 32) | type PasscodeHandler struct
    method Init (line 72) | func (h *PasscodeHandler) Init(c echo.Context) error {
    method Finish (line 265) | func (h *PasscodeHandler) Finish(c echo.Context) error {
    method GetSessionToken (line 470) | func (h *PasscodeHandler) GetSessionToken(c echo.Context) jwt.Token {
  function NewPasscodeHandler (line 48) | func NewPasscodeHandler(cfg *config.Config, persister persistence.Persis...

FILE: backend/handler/passcode_test.go
  function TestPasscodeSuite (line 23) | func TestPasscodeSuite(t *testing.T) {
  type passcodeSuite (line 29) | type passcodeSuite struct
    method TestPasscodeHandler_Init (line 33) | func (s *passcodeSuite) TestPasscodeHandler_Init() {
    method TestPasscodeHandler_Finish (line 115) | func (s *passcodeSuite) TestPasscodeHandler_Finish() {

FILE: backend/handler/password.go
  type PasswordHandler (line 24) | type PasswordHandler struct
    method Set (line 51) | func (h *PasswordHandler) Set(c echo.Context) error {
    method Login (line 158) | func (h *PasswordHandler) Login(c echo.Context) error {
  function NewPasswordHandler (line 32) | func NewPasswordHandler(persister persistence.Persister, sessionManager ...
  type PasswordSetBody (line 46) | type PasswordSetBody struct
  type PasswordLoginBody (line 153) | type PasswordLoginBody struct

FILE: backend/handler/password_admin.go
  type PasswordAdminHandler (line 13) | type PasswordAdminHandler interface
  type passwordAdminHandler (line 20) | type passwordAdminHandler struct
    method Get (line 32) | func (h *passwordAdminHandler) Get(ctx echo.Context) error {
    method Create (line 70) | func (h *passwordAdminHandler) Create(ctx echo.Context) error {
    method Update (line 118) | func (h *passwordAdminHandler) Update(ctx echo.Context) error {
    method Delete (line 166) | func (h *passwordAdminHandler) Delete(ctx echo.Context) error {
  function NewPasswordAdminHandler (line 25) | func NewPasswordAdminHandler(persister persistence.Persister) PasswordAd...

FILE: backend/handler/password_admin_test.go
  function TestPasswordAdminSuite (line 16) | func TestPasswordAdminSuite(t *testing.T) {
  type passwordAdminSuite (line 21) | type passwordAdminSuite struct
    method TestPasswordAdminHandler_Get (line 25) | func (s *passwordAdminSuite) TestPasswordAdminHandler_Get() {
    method TestPasswordAdminHandler_Create (line 85) | func (s *passwordAdminSuite) TestPasswordAdminHandler_Create() {
    method TestPasswordAdminHandler_Update (line 171) | func (s *passwordAdminSuite) TestPasswordAdminHandler_Update() {
    method TestPasswordAdminHandler_Delete (line 259) | func (s *passwordAdminSuite) TestPasswordAdminHandler_Delete() {

FILE: backend/handler/password_test.go
  function TestPasswordSuite (line 17) | func TestPasswordSuite(t *testing.T) {
  type passwordSuite (line 22) | type passwordSuite struct
    method TestPasswordHandler_Set_Create (line 26) | func (s *passwordSuite) TestPasswordHandler_Set_Create() {
    method TestPasswordHandler_Login (line 107) | func (s *passwordSuite) TestPasswordHandler_Login() {
  function TestMaxPasswordLength (line 195) | func TestMaxPasswordLength(t *testing.T) {

FILE: backend/handler/public_router.go
  function NewPublicRouter (line 27) | func NewPublicRouter(cfg *config.Config, persister persistence.Persister...

FILE: backend/handler/session.go
  type SessionHandler (line 15) | type SessionHandler struct
    method ValidateSession (line 29) | func (h *SessionHandler) ValidateSession(c echo.Context) error {
    method ValidateSessionFromBody (line 80) | func (h *SessionHandler) ValidateSessionFromBody(c echo.Context) error {
  function NewSessionHandler (line 21) | func NewSessionHandler(persister persistence.Persister, sessionManager s...

FILE: backend/handler/session_admin.go
  type SessionAdminHandler (line 20) | type SessionAdminHandler struct
    method Generate (line 36) | func (h *SessionAdminHandler) Generate(ctx echo.Context) error {
    method List (line 117) | func (h *SessionAdminHandler) List(ctx echo.Context) error {
    method Delete (line 145) | func (h *SessionAdminHandler) Delete(ctx echo.Context) error {
  function NewSessionAdminHandler (line 27) | func NewSessionAdminHandler(cfg *config.Config, persister persistence.Pe...

FILE: backend/handler/session_admin_test.go
  function TestSessionAdminSuite (line 15) | func TestSessionAdminSuite(t *testing.T) {
  type sessionAdminSuite (line 20) | type sessionAdminSuite struct
    method TestSessionAdminHandler_List (line 24) | func (s *sessionAdminSuite) TestSessionAdminHandler_List() {
    method TestSessionAdminHandler_Delete (line 97) | func (s *sessionAdminSuite) TestSessionAdminHandler_Delete() {

FILE: backend/handler/status.go
  type StatusHandler (line 9) | type StatusHandler struct
    method Status (line 19) | func (h *StatusHandler) Status(c echo.Context) error {
  function NewStatusHandler (line 13) | func NewStatusHandler(persister persistence.Persister) *StatusHandler {

FILE: backend/handler/thirdparty.go
  type ThirdPartyHandler (line 24) | type ThirdPartyHandler struct
    method Auth (line 40) | func (h *ThirdPartyHandler) Auth(c echo.Context) error {
    method CallbackPost (line 84) | func (h *ThirdPartyHandler) CallbackPost(c echo.Context) error {
    method Callback (line 92) | func (h *ThirdPartyHandler) Callback(c echo.Context) error {
    method redirectError (line 241) | func (h *ThirdPartyHandler) redirectError(c echo.Context, error error,...
    method auditError (line 256) | func (h *ThirdPartyHandler) auditError(c echo.Context, err error) error {
  function NewThirdPartyHandler (line 31) | func NewThirdPartyHandler(cfg *config.Config, persister persistence.Pers...

FILE: backend/handler/thirdparty_auth_test.go
  method TestThirdPartyHandler_Auth (line 12) | func (s *thirdPartySuite) TestThirdPartyHandler_Auth() {

FILE: backend/handler/thirdparty_callback_error_test.go
  method TestThirdPartyHandler_Callback_Error_LinkingNotAllowedForProvider (line 14) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_LinkingNo...
  method TestThirdPartyHandler_Callback_Error_SignInMultipleAccounts (line 66) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_SignInMul...
  method TestThirdPartyHandler_Callback_Error_NoState (line 117) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_NoState() {
  method TestThirdPartyHandler_Callback_Error_StateMismatch (line 143) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_StateMism...
  method TestThirdPartyHandler_Callback_Error_NoThirdPartyCookie (line 179) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_NoThirdPa...
  method TestThirdPartyHandler_Callback_Error_ProviderError (line 208) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_ProviderE...
  method TestThirdPartyHandler_Callback_Error_ProviderDisabled (line 241) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_ProviderD...
  method TestThirdPartyHandler_Callback_Error_NoAuthCode (line 274) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_NoAuthCod...
  method TestThirdPartyHandler_Callback_Error_OAuthTokenExchange (line 307) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_OAuthToke...
  method TestThirdPartyHandler_Callback_Error_VerificationRequiredUnverifiedProviderEmail (line 346) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_Verificat...
  method TestThirdPartyHandler_Callback_Error_MicrosoftUnverifiedEmail (line 394) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Error_Microsoft...

FILE: backend/handler/thirdparty_callback_test.go
  method TestThirdPartyHandler_Callback_SignUp_Google (line 14) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Google() {
  method TestThirdPartyHandler_Callback_SignIn_Google (line 72) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Google() {
  method TestThirdPartyHandler_Callback_SignUp_GitHub (line 133) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_GitHub() {
  method TestThirdPartyHandler_Callback_SignIn_GitHub (line 201) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_GitHub() {
  method TestThirdPartyHandler_Callback_SignUp_Apple (line 272) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Apple() {
  method TestThirdPartyHandler_Callback_SignIn_Apple (line 328) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Apple() {
  method TestThirdPartyHandler_Callback_SignIn_Apple_WithBooleanEmailVerifiedClaim (line 387) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Apple_Wi...
  method TestThirdPartyHandler_Callback_SignUp_Discord (line 446) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Discord() {
  method TestThirdPartyHandler_Callback_SignIn_Discord (line 504) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Discord() {
  method TestThirdPartyHandler_Callback_SignUp_Microsoft (line 565) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Microsof...
  method TestThirdPartyHandler_Callback_SignIn_Microsoft (line 621) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Microsof...
  method TestThirdPartyHandler_Callback_SignUp_Facebook (line 680) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_Facebook...
  method TestThirdPartyHandler_Callback_SignIn_Facebook (line 737) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Facebook...
  method TestThirdPartyHandler_Callback_SignUp_WithUnclaimedEmail (line 797) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignUp_WithUncl...
  method TestThirdPartyHandler_Callback_SignIn_ProviderEMailChangedToExistingEmail (line 858) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Provider...
  method TestThirdPartyHandler_Callback_SignIn_ProviderEMailChangedToUnclaimedEmail (line 919) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Provider...
  method TestThirdPartyHandler_Callback_SignIn_ProviderEMailChangedToNonExistentEmail (line 980) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_SignIn_Provider...
  method TestThirdPartyHandler_Callback_Link_ExistingAccountNoIdentities (line 1042) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Link_ExistingAc...
  method TestThirdPartyHandler_Callback_Link_GoogleToAccountWithGithubIdentity (line 1104) | func (s *thirdPartySuite) TestThirdPartyHandler_Callback_Link_GoogleToAc...

FILE: backend/handler/thirdparty_test.go
  function TestThirdPartySuite (line 25) | func TestThirdPartySuite(t *testing.T) {
  type thirdPartySuite (line 30) | type thirdPartySuite struct
    method setUpContext (line 34) | func (s *thirdPartySuite) setUpContext(request *http.Request) (echo.Co...
    method setUpHandler (line 43) | func (s *thirdPartySuite) setUpHandler(cfg *config.Config) *ThirdParty...
    method setUpConfig (line 57) | func (s *thirdPartySuite) setUpConfig(enabledProviders []string, allow...
    method setUpFakeJwkSet (line 138) | func (s *thirdPartySuite) setUpFakeJwkSet() jwk2.Set {
    method setUpAppleIdToken (line 146) | func (s *thirdPartySuite) setUpAppleIdToken(sub, aud, email string, em...
    method setUpMicrosoftIdToken (line 170) | func (s *thirdPartySuite) setUpMicrosoftIdToken(sub, aud, email string...
    method assertLocationHeaderHasToken (line 190) | func (s *thirdPartySuite) assertLocationHeaderHasToken(rec *httptest.R...
    method assertStateCookieRemoved (line 198) | func (s *thirdPartySuite) assertStateCookieRemoved(rec *httptest.Respo...

FILE: backend/handler/token.go
  type TokenHandler (line 22) | type TokenHandler struct
    method Validate (line 48) | func (h TokenHandler) Validate(c echo.Context) error {
  function NewTokenHandler (line 30) | func NewTokenHandler(cfg *config.Config, persister persistence.Persister...
  type TokenValidationBody (line 44) | type TokenValidationBody struct

FILE: backend/handler/token_test.go
  function TestTokenSuite (line 17) | func TestTokenSuite(t *testing.T) {
  type tokenSuite (line 22) | type tokenSuite struct
    method TestToken_Validate_TokenInCookie (line 26) | func (s *tokenSuite) TestToken_Validate_TokenInCookie() {
    method TestToken_Validate_TokenInHeader (line 73) | func (s *tokenSuite) TestToken_Validate_TokenInHeader() {
    method TestToken_Validate_ExpiredToken (line 114) | func (s *tokenSuite) TestToken_Validate_ExpiredToken() {
    method TestToken_Validate_MissingTokenFromRequest (line 144) | func (s *tokenSuite) TestToken_Validate_MissingTokenFromRequest() {
    method TestToken_Validate_InvalidJson (line 167) | func (s *tokenSuite) TestToken_Validate_InvalidJson() {
    method TestToken_Validate_TokenNotFound (line 187) | func (s *tokenSuite) TestToken_Validate_TokenNotFound() {
    method setupConfig (line 218) | func (s *tokenSuite) setupConfig() *config.Config {

FILE: backend/handler/user.go
  type UserHandler (line 24) | type UserHandler struct
    method Create (line 44) | func (h *UserHandler) Create(c echo.Context) error {
    method Get (line 175) | func (h *UserHandler) Get(c echo.Context) error {
    method GetUserIdByEmail (line 222) | func (h *UserHandler) GetUserIdByEmail(c echo.Context) error {
    method Me (line 255) | func (h *UserHandler) Me(c echo.Context) error {
    method Delete (line 274) | func (h *UserHandler) Delete(c echo.Context) error {
    method Logout (line 321) | func (h *UserHandler) Logout(c echo.Context) error {
  function NewUserHandler (line 31) | func NewUserHandler(cfg *config.Config, persister persistence.Persister,...
  type UserCreateBody (line 40) | type UserCreateBody struct
  type UserGetByEmailBody (line 218) | type UserGetByEmailBody struct

FILE: backend/handler/user_admin.go
  type UserHandlerAdmin (line 31) | type UserHandlerAdmin struct
    method Delete (line 39) | func (h *UserHandlerAdmin) Delete(c echo.Context) error {
    method List (line 84) | func (h *UserHandlerAdmin) List(c echo.Context) error {
    method Get (line 146) | func (h *UserHandlerAdmin) Get(c echo.Context) error {
    method Create (line 165) | func (h *UserHandlerAdmin) Create(c echo.Context) error {
    method Patch (line 345) | func (h *UserHandlerAdmin) Patch(c echo.Context) error {
  function NewUserHandlerAdmin (line 35) | func NewUserHandlerAdmin(persister persistence.Persister) *UserHandlerAd...
  type UserListRequest (line 75) | type UserListRequest struct
  type OptionalString (line 315) | type OptionalString struct
    method UnmarshalJSON (line 320) | func (o *OptionalString) UnmarshalJSON(b []byte) error {
  type PatchUserAdminRequest (line 337) | type PatchUserAdminRequest struct

FILE: backend/handler/user_admin_test.go
  function TestUserHandlerAdminSuite (line 16) | func TestUserHandlerAdminSuite(t *testing.T) {
  type userAdminSuite (line 21) | type userAdminSuite struct
    method TestUserHandlerAdmin_Delete (line 25) | func (s *userAdminSuite) TestUserHandlerAdmin_Delete() {
    method TestUserHandlerAdmin_Delete_UnknownUserId (line 46) | func (s *userAdminSuite) TestUserHandlerAdmin_Delete_UnknownUserId() {
    method TestUserHandlerAdmin_Delete_InvalidUserId (line 67) | func (s *userAdminSuite) TestUserHandlerAdmin_Delete_InvalidUserId() {
    method TestUserHandlerAdmin_List (line 78) | func (s *userAdminSuite) TestUserHandlerAdmin_List() {
    method TestUserHandlerAdmin_List_Pagination (line 97) | func (s *userAdminSuite) TestUserHandlerAdmin_List_Pagination() {
    method TestUserHandlerAdmin_List_NoUsers (line 123) | func (s *userAdminSuite) TestUserHandlerAdmin_List_NoUsers() {
    method TestUserHandlerAdmin_List_MultipleUserIDs (line 147) | func (s *userAdminSuite) TestUserHandlerAdmin_List_MultipleUserIDs() {
    method TestUserHandlerAdmin_List_InvalidPaginationParam (line 174) | func (s *userAdminSuite) TestUserHandlerAdmin_List_InvalidPaginationPa...
    method TestUserHandlerAdmin_Create (line 185) | func (s *userAdminSuite) TestUserHandlerAdmin_Create() {
    method TestUserHandlerAdmin_Patch_Success (line 282) | func (s *userAdminSuite) TestUserHandlerAdmin_Patch_Success() {
    method TestUserHandlerAdmin_Patch_Failure (line 372) | func (s *userAdminSuite) TestUserHandlerAdmin_Patch_Failure() {

FILE: backend/handler/user_test.go
  function TestUserSuite (line 21) | func TestUserSuite(t *testing.T) {
  type userSuite (line 26) | type userSuite struct
    method TestUserHandler_Create_TokenInCookie (line 30) | func (s *userSuite) TestUserHandler_Create_TokenInCookie() {
    method TestUserHandler_Create_TokenInHeader (line 74) | func (s *userSuite) TestUserHandler_Create_TokenInHeader() {
    method TestUserHandler_Create_CaseInsensitive (line 117) | func (s *userSuite) TestUserHandler_Create_CaseInsensitive() {
    method TestUserHandler_Create_UserExists (line 149) | func (s *userSuite) TestUserHandler_Create_UserExists() {
    method TestUserHandler_Create_UserExists_CaseInsensitive (line 171) | func (s *userSuite) TestUserHandler_Create_UserExists_CaseInsensitive() {
    method TestUserHandler_Create_InvalidEmail (line 193) | func (s *userSuite) TestUserHandler_Create_InvalidEmail() {
    method TestUserHandler_Create_EmailMissing (line 208) | func (s *userSuite) TestUserHandler_Create_EmailMissing() {
    method TestUserHandler_Create_AccountCreationDisabled (line 223) | func (s *userSuite) TestUserHandler_Create_AccountCreationDisabled() {
    method TestUserHandler_Get (line 244) | func (s *userSuite) TestUserHandler_Get() {
    method TestUserHandler_GetUserWithWebAuthnCredential (line 274) | func (s *userSuite) TestUserHandler_GetUserWithWebAuthnCredential() {
    method TestUserHandler_Get_InvalidUserId (line 309) | func (s *userSuite) TestUserHandler_Get_InvalidUserId() {
    method TestUserHandler_GetUserIdByEmail_InvalidEmail (line 333) | func (s *userSuite) TestUserHandler_GetUserIdByEmail_InvalidEmail() {
    method TestUserHandler_GetUserIdByEmail_InvalidJson (line 348) | func (s *userSuite) TestUserHandler_GetUserIdByEmail_InvalidJson() {
    method TestUserHandler_GetUserIdByEmail_UserNotFound (line 363) | func (s *userSuite) TestUserHandler_GetUserIdByEmail_UserNotFound() {
    method TestUserHandler_GetUserIdByEmail (line 378) | func (s *userSuite) TestUserHandler_GetUserIdByEmail() {
    method TestUserHandler_GetUserIdByEmail_CaseInsensitive (line 407) | func (s *userSuite) TestUserHandler_GetUserIdByEmail_CaseInsensitive() {
    method TestUserHandler_Me (line 436) | func (s *userSuite) TestUserHandler_Me() {
    method TestUserHandler_Logout (line 492) | func (s *userSuite) TestUserHandler_Logout() {
    method TestUserHandler_Delete (line 521) | func (s *userSuite) TestUserHandler_Delete() {

FILE: backend/handler/utils.go
  function loadDto (line 16) | func loadDto[I any](ctx echo.Context) (*I, error) {
  function storeSession (line 33) | func storeSession(cfg *config.Config, persister persistence.Persister, u...

FILE: backend/handler/webauthn.go
  type WebauthnHandler (line 27) | type WebauthnHandler struct
    method BeginRegistration (line 88) | func (h *WebauthnHandler) BeginRegistration(c echo.Context) error {
    method FinishRegistration (line 141) | func (h *WebauthnHandler) FinishRegistration(c echo.Context) error {
    method BeginAuthentication (line 240) | func (h *WebauthnHandler) BeginAuthentication(c echo.Context) error {
    method FinishAuthentication (line 313) | func (h *WebauthnHandler) FinishAuthentication(c echo.Context) error {
    method ListCredentials (line 489) | func (h *WebauthnHandler) ListCredentials(c echo.Context) error {
    method UpdateCredential (line 514) | func (h *WebauthnHandler) UpdateCredential(c echo.Context) error {
    method DeleteCredential (line 565) | func (h *WebauthnHandler) DeleteCredential(c echo.Context) error {
    method getWebauthnUser (line 607) | func (h WebauthnHandler) getWebauthnUser(connection *pop.Connection, u...
  constant GetUserFailureMessage (line 37) | GetUserFailureMessage               = "failed to get user: %w"
  constant CastSessionFailureMessage (line 38) | CastSessionFailureMessage           = "failed to cast session object"
  constant CreateAuditLogFailureMessage (line 39) | CreateAuditLogFailureMessage        = "failed to create audit log: %w"
  constant UserNotFoundMessage (line 40) | UserNotFoundMessage                 = "user not found"
  constant SubjectParseFailureMessage (line 41) | SubjectParseFailureMessage          = "failed to parse subject as uuid: %w"
  constant GetWebauthnCredentialFailureMessage (line 42) | GetWebauthnCredentialFailureMessage = "failed to get webauthn credential...
  constant StoredChallengeMismatchMessage (line 43) | StoredChallengeMismatchMessage      = "Stored challenge and received cha...
  constant UnknownUserMessage (line 44) | UnknownUserMessage                  = "unknown user"
  function NewWebauthnHandler (line 48) | func NewWebauthnHandler(cfg *config.Config, persister persistence.Persis...
  type BeginAuthenticationBody (line 235) | type BeginAuthenticationBody struct

FILE: backend/handler/webauthn_credential_admin.go
  type WebauthnCredentialAdminHandler (line 13) | type WebauthnCredentialAdminHandler interface
  type webauthnCredentialAdminHandler (line 19) | type webauthnCredentialAdminHandler struct
    method List (line 29) | func (h *webauthnCredentialAdminHandler) List(ctx echo.Context) error {
    method Get (line 62) | func (h *webauthnCredentialAdminHandler) Get(ctx echo.Context) error {
    method Delete (line 94) | func (h *webauthnCredentialAdminHandler) Delete(ctx echo.Context) error {
  function NewWebauthnCredentialAdminHandler (line 23) | func NewWebauthnCredentialAdminHandler(persister persistence.Persister) ...

FILE: backend/handler/webauthn_credential_admin_test.go
  function TestWebauthnCredentialAdminSuite (line 15) | func TestWebauthnCredentialAdminSuite(t *testing.T) {
  type webauthnCredentialAdminSuite (line 20) | type webauthnCredentialAdminSuite struct
    method TestWebauthnCredentialAdminHandler_List (line 24) | func (s *webauthnCredentialAdminSuite) TestWebauthnCredentialAdminHand...
    method TestWebauthnCredentialAdminHandler_Get (line 94) | func (s *webauthnCredentialAdminSuite) TestWebauthnCredentialAdminHand...
    method TestWebauthnCredentialAdminHandler_Delete (line 172) | func (s *webauthnCredentialAdminSuite) TestWebauthnCredentialAdminHand...

FILE: backend/handler/webauthn_test.go
  function TestWebauthnSuite (line 19) | func TestWebauthnSuite(t *testing.T) {
  type webauthnSuite (line 24) | type webauthnSuite struct
    method TestWebauthnHandler_NewHandler (line 28) | func (s *webauthnSuite) TestWebauthnHandler_NewHandler() {
    method TestWebauthnHandler_BeginRegistration (line 38) | func (s *webauthnSuite) TestWebauthnHandler_BeginRegistration() {
    method TestWebauthnHandler_FinalizeRegistration (line 76) | func (s *webauthnSuite) TestWebauthnHandler_FinalizeRegistration() {
    method TestWebauthnHandler_FinalizeRegistration_SessionDataExpired (line 119) | func (s *webauthnSuite) TestWebauthnHandler_FinalizeRegistration_Sessi...
    method TestWebauthnHandler_BeginAuthentication (line 153) | func (s *webauthnSuite) TestWebauthnHandler_BeginAuthentication() {
    method TestWebauthnHandler_FinalizeAuthentication (line 177) | func (s *webauthnSuite) TestWebauthnHandler_FinalizeAuthentication() {
    method TestWebauthnHandler_FinalizeAuthentication_SessionDataExpired (line 230) | func (s *webauthnSuite) TestWebauthnHandler_FinalizeAuthentication_Ses...
    method TestWebauthnHandler_FinalizeAuthentication_TokenInHeader (line 260) | func (s *webauthnSuite) TestWebauthnHandler_FinalizeAuthentication_Tok...
    method TestWebauthnHandler_FinalizeAuthentication_SignCountMismatch (line 310) | func (s *webauthnSuite) TestWebauthnHandler_FinalizeAuthentication_Sig...

FILE: backend/handler/webhook.go
  type WebhookHandler (line 19) | type WebhookHandler interface
  constant uuidErrorFormat (line 28) | uuidErrorFormat = "unable to create uuid: %w"
  type webhookHandler (line 31) | type webhookHandler struct
    method List (line 43) | func (w *webhookHandler) List(ctx echo.Context) error {
    method Create (line 59) | func (w *webhookHandler) Create(ctx echo.Context) error {
    method createWebhookEvents (line 110) | func (w *webhookHandler) createWebhookEvents(evts events.Events, webho...
    method Get (line 132) | func (w *webhookHandler) Get(ctx echo.Context) error {
    method Delete (line 156) | func (w *webhookHandler) Delete(ctx echo.Context) error {
    method Update (line 188) | func (w *webhookHandler) Update(ctx echo.Context) error {
    method getWebhook (line 245) | func (w *webhookHandler) getWebhook(id uuid.UUID, persister persistenc...
  function NewWebhookHandler (line 36) | func NewWebhookHandler(cfg config.WebhookSettings, persister persistence...

FILE: backend/handler/webhook_test.go
  function TestWebhookHandlerSuite (line 20) | func TestWebhookHandlerSuite(t *testing.T) {
  type webhookSuite (line 25) | type webhookSuite struct
    method TestWebhookHandler_List (line 29) | func (s *webhookSuite) TestWebhookHandler_List() {
    method TestWebhookHandler_Create (line 69) | func (s *webhookSuite) TestWebhookHandler_Create() {
    method TestWebhookHandler_CreateWithParams (line 110) | func (s *webhookSuite) TestWebhookHandler_CreateWithParams() {
    method TestWebhookHandler_Delete (line 197) | func (s *webhookSuite) TestWebhookHandler_Delete() {
    method TestWebhookHandler_DeleteWithParams (line 234) | func (s *webhookSuite) TestWebhookHandler_DeleteWithParams() {
    method TestWebhookHandler_Get (line 292) | func (s *webhookSuite) TestWebhookHandler_Get() {
    method TestWebhookHandler_GetWithParams (line 325) | func (s *webhookSuite) TestWebhookHandler_GetWithParams() {
    method TestWebhookHandler_Update (line 393) | func (s *webhookSuite) TestWebhookHandler_Update() {
    method TestWebhookHandler_UpdateWithParams (line 455) | func (s *webhookSuite) TestWebhookHandler_UpdateWithParams() {

FILE: backend/handler/well_known.go
  type WellKnownHandler (line 11) | type WellKnownHandler struct
    method GetPublicKeys (line 23) | func (h *WellKnownHandler) GetPublicKeys(c echo.Context) error {
    method GetConfig (line 33) | func (h *WellKnownHandler) GetConfig(c echo.Context) error {
  function NewWellKnownHandler (line 16) | func NewWellKnownHandler(config config.Config, jwkManager hankoJwk.Manag...

FILE: backend/handler/well_known_test.go
  function TestWellKnownSuite (line 11) | func TestWellKnownSuite(t *testing.T) {
  type wellKnownSuite (line 16) | type wellKnownSuite struct
    method TestWellKnownHandler_GetPublicKeys (line 20) | func (s *wellKnownSuite) TestWellKnownHandler_GetPublicKeys() {

FILE: backend/mail/mailer.go
  type Mailer (line 10) | type Mailer interface
  type mailer (line 14) | type mailer struct
    method Send (line 29) | func (m *mailer) Send(message *gomail.Message) error {
  function NewMailer (line 18) | func NewMailer(config config.SMTP) (Mailer, error) {

FILE: backend/mail/mailer_test.go
  function TestNewMailer (line 9) | func TestNewMailer(t *testing.T) {

FILE: backend/mail/render.go
  type Renderer (line 17) | type Renderer struct
    method translate (line 50) | func (r *Renderer) translate(messageID string, templateData map[string...
    method RenderPlain (line 60) | func (r *Renderer) RenderPlain(templateName string, lang string, data ...
    method RenderHTML (line 73) | func (r *Renderer) RenderHTML(templateName string, lang string, data m...
    method Translate (line 94) | func (r *Renderer) Translate(lang string, messageID string, data map[s...
  function NewRenderer (line 24) | func NewRenderer() (*Renderer, error) {

FILE: backend/mail/render_test.go
  function TestNewRenderer (line 8) | func TestNewRenderer(t *testing.T) {
  function TestRenderer_RenderPlain (line 15) | func TestRenderer_RenderPlain(t *testing.T) {
  function TestRenderer_RenderHTML (line 77) | func TestRenderer_RenderHTML(t *testing.T) {
  function TestRenderer_Translate (line 141) | func TestRenderer_Translate(t *testing.T) {

FILE: backend/main.go
  function main (line 10) | func main() {

FILE: backend/mapper/authenticator_mapper.go
  type Authenticator (line 16) | type Authenticator struct
  type AuthenticatorMetadata (line 22) | type AuthenticatorMetadata
    method GetNameForAaguid (line 24) | func (am AuthenticatorMetadata) GetNameForAaguid(aaguid uuid.UUID) *st...
  function LoadAuthenticatorMetadata (line 34) | func LoadAuthenticatorMetadata(authMetaFilePath *string) AuthenticatorMe...
  function loadFromEmbeddedFile (line 63) | func loadFromEmbeddedFile() (AuthenticatorMetadata, error) {

FILE: backend/middleware/logger.go
  function GetLoggerMiddleware (line 8) | func GetLoggerMiddleware() echo.MiddlewareFunc {

FILE: backend/middleware/session.go
  function Session (line 17) | func Session(cfg *config.Config, persister persistence.Persister, genera...
  function parseToken (line 31) | func parseToken(cfg config.Session, persister persistence.Persister, gen...

FILE: backend/middleware/webhook.go
  function WebhookMiddleware (line 11) | func WebhookMiddleware(cfg *config.Config, jwkManager jwk.Generator, per...

FILE: backend/pagination/header.go
  function CreateHeader (line 9) | func CreateHeader(u *url.URL, total int, page int, itemsPerPage int) str...
  function formatter (line 42) | func formatter(u *url.URL, rel string, perPage int, page int) string {

FILE: backend/pagination/header_test.go
  function TestCreateHeader_FirstPage (line 9) | func TestCreateHeader_FirstPage(t *testing.T) {
  function TestCreateHeader_LastPage (line 15) | func TestCreateHeader_LastPage(t *testing.T) {
  function TestCreateHeader_MiddlePage (line 21) | func TestCreateHeader_MiddlePage(t *testing.T) {
  function TestCreateHeader_TotalCountZero (line 27) | func TestCreateHeader_TotalCountZero(t *testing.T) {
  function TestCreateHeader_ItemsPerPageGreaterTotalCount (line 33) | func TestCreateHeader_ItemsPerPageGreaterTotalCount(t *testing.T) {
  function TestCreateHeader_UrlWithQuery (line 39) | func TestCreateHeader_UrlWithQuery(t *testing.T) {

FILE: backend/persistence/audit_log_persister.go
  type AuditLogPersister (line 14) | type AuditLogPersister interface
  type auditLogPersister (line 22) | type auditLogPersister struct
    method Create (line 30) | func (p *auditLogPersister) Create(auditLog models.AuditLog) error {
    method Get (line 42) | func (p *auditLogPersister) Get(id uuid.UUID) (*models.AuditLog, error) {
    method List (line 55) | func (p *auditLogPersister) List(page int, perPage int, startTime *tim...
    method Count (line 72) | func (p *auditLogPersister) Count(startTime *time.Time, endTime *time....
    method addQueryParamsToSqlQuery (line 83) | func (p *auditLogPersister) addQueryParamsToSqlQuery(query *pop.Query,...
    method FindExpired (line 126) | func (p *auditLogPersister) FindExpired(cutoffTime time.Time, page, pe...
    method Delete (line 138) | func (p *auditLogPersister) Delete(auditLog models.AuditLog) error {
  function NewAuditLogPersister (line 26) | func NewAuditLogPersister(db *pop.Connection) AuditLogPersister {

FILE: backend/persistence/email_persister.go
  type EmailPersister (line 12) | type EmailPersister interface
  type emailPersister (line 22) | type emailPersister struct
    method Get (line 30) | func (e *emailPersister) Get(emailId uuid.UUID) (*models.Email, error) {
    method FindByUserId (line 44) | func (e *emailPersister) FindByUserId(userId uuid.UUID) (models.Emails...
    method CountByUserId (line 62) | func (e *emailPersister) CountByUserId(userId uuid.UUID) (int, error) {
    method FindByAddress (line 76) | func (e *emailPersister) FindByAddress(address string) (*models.Email,...
    method Create (line 93) | func (e *emailPersister) Create(email models.Email) error {
    method Update (line 106) | func (e *emailPersister) Update(email models.Email) error {
    method Delete (line 119) | func (e *emailPersister) Delete(email models.Email) error {
  function NewEmailPersister (line 26) | func NewEmailPersister(db *pop.Connection) EmailPersister {

FILE: backend/persistence/flow_persister.go
  type FlowPersister (line 12) | type FlowPersister interface
  type flowPersister (line 17) | type flowPersister struct
    method GetFlow (line 25) | func (p flowPersister) GetFlow(flowID uuid.UUID) (*flowpilot.FlowModel...
    method CreateFlow (line 36) | func (p flowPersister) CreateFlow(flowModel flowpilot.FlowModel) error {
    method UpdateFlow (line 55) | func (p flowPersister) UpdateFlow(flowModel flowpilot.FlowModel) error {
    method FindExpired (line 83) | func (p flowPersister) FindExpired(cutoffTime time.Time, page, perPage...
    method Delete (line 95) | func (p flowPersister) Delete(item models.Flow) error {
  function NewFlowPersister (line 21) | func NewFlowPersister(tx *pop.Connection) FlowPersister {

FILE: backend/persistence/identity_persister.go
  type IdentityPersister (line 12) | type IdentityPersister interface
  type identityPersister (line 20) | type identityPersister struct
    method GetByID (line 24) | func (p identityPersister) GetByID(identityID uuid.UUID) (*models.Iden...
    method Get (line 35) | func (p identityPersister) Get(providerUserID string, providerID strin...
    method Create (line 46) | func (p identityPersister) Create(identity models.Identity) error {
    method Update (line 59) | func (p identityPersister) Update(identity models.Identity) error {
    method Delete (line 72) | func (p identityPersister) Delete(identity models.Identity) error {
  function NewIdentityPersister (line 81) | func NewIdentityPersister(db *pop.Connection) IdentityPersister {

FILE: backend/persistence/jwk_persister.go
  type JwkPersister (line 11) | type JwkPersister interface
  type jwkPersister (line 18) | type jwkPersister struct
    method Get (line 26) | func (p *jwkPersister) Get(id int) (*models.Jwk, error) {
    method GetAll (line 38) | func (p *jwkPersister) GetAll() ([]models.Jwk, error) {
    method GetLast (line 50) | func (p *jwkPersister) GetLast() (*models.Jwk, error) {
    method Create (line 62) | func (p *jwkPersister) Create(jwk models.Jwk) error {
  function NewJwkPersister (line 22) | func NewJwkPersister(db *pop.Connection) JwkPersister {

FILE: backend/persistence/models/audit_log.go
  type AuditLog (line 11) | type AuditLog struct
  type Details (line 25) | type Details
  type RequestMeta (line 27) | type RequestMeta struct
  function NewAuditLog (line 33) | func NewAuditLog(auditLogType AuditLogType, requestMeta RequestMeta, det...
  type AuditLogType (line 72) | type AuditLogType

FILE: backend/persistence/models/email.go
  type Email (line 13) | type Email struct
    method IsPrimary (line 41) | func (email *Email) IsPrimary() bool {
    method GetSamlIdentityForDomain (line 48) | func (email *Email) GetSamlIdentityForDomain(domain string) *SamlIdent...
    method Validate (line 101) | func (email *Email) Validate(tx *pop.Connection) (*validate.Errors, er...
  type Emails (line 25) | type Emails
    method GetVerified (line 57) | func (emails *Emails) GetVerified() Emails {
    method HasUnverified (line 67) | func (emails *Emails) HasUnverified() bool {
    method GetPrimary (line 73) | func (emails *Emails) GetPrimary() *Email {
    method GetEmailByAddress (line 82) | func (emails *Emails) GetEmailByAddress(address string) *Email {
    method GetEmailById (line 91) | func (emails *Emails) GetEmailById(emailId uuid.UUID) *Email {
  function NewEmail (line 27) | func NewEmail(userId *uuid.UUID, address string) *Email {

FILE: backend/persistence/models/flow.go
  type Flow (line 13) | type Flow struct
    method ToFlowpilotModel (line 23) | func (f *Flow) ToFlowpilotModel() *flowpilot.FlowModel {
    method Validate (line 42) | func (f *Flow) Validate(tx *pop.Connection) (*validate.Errors, error) {
    method ValidateCreate (line 48) | func (f *Flow) ValidateCreate(tx *pop.Connection) (*validate.Errors, e...
    method ValidateUpdate (line 54) | func (f *Flow) ValidateUpdate(tx *pop.Connection) (*validate.Errors, e...
  type Flows (line 38) | type Flows

FILE: backend/persistence/models/identity.go
  type Identity (line 15) | type Identity struct
    method Validate (line 64) | func (i *Identity) Validate(tx *pop.Connection) (*validate.Errors, err...
  type Identities (line 28) | type Identities
    method GetIdentity (line 30) | func (identities Identities) GetIdentity(providerID string, providerUs...
  function NewIdentity (line 40) | func NewIdentity(providerID string, identityData map[string]interface{},...

FILE: backend/persistence/models/jwk.go
  type Jwk (line 10) | type Jwk struct
    method Validate (line 17) | func (jwk *Jwk) Validate(tx *pop.Connection) (*validate.Errors, error) {

FILE: backend/persistence/models/otp_secret.go
  type OTPSecret (line 11) | type OTPSecret struct
    method TableName (line 19) | func (otpSecret OTPSecret) TableName() string {
    method Validate (line 34) | func (otpSecret *OTPSecret) Validate(tx *pop.Connection) (*validate.Er...
  function NewOTPSecret (line 23) | func NewOTPSecret(userID uuid.UUID, secret string) *OTPSecret {

FILE: backend/persistence/models/passcode.go
  type Passcode (line 12) | type Passcode struct
    method Validate (line 25) | func (passcode *Passcode) Validate(tx *pop.Connection) (*validate.Erro...

FILE: backend/persistence/models/password_credential.go
  type PasswordCredential (line 11) | type PasswordCredential struct
    method Validate (line 30) | func (password *PasswordCredential) Validate(tx *pop.Connection) (*val...
  function NewPasswordCredential (line 19) | func NewPasswordCredential(userId uuid.UUID, password string) *PasswordC...

FILE: backend/persistence/models/primary_email.go
  type PrimaryEmail (line 11) | type PrimaryEmail struct
    method Validate (line 34) | func (primaryEmail *PrimaryEmail) Validate(tx *pop.Connection) (*valid...
  function NewPrimaryEmail (line 21) | func NewPrimaryEmail(emailId uuid.UUID, userId uuid.UUID) *PrimaryEmail {

FILE: backend/persistence/models/saml_certificate.go
  type SamlCertificate (line 22) | type SamlCertificate struct
    method DecryptCertKey (line 115) | func (s *SamlCertificate) DecryptCertKey() ([]byte, error) {
    method Validate (line 130) | func (s *SamlCertificate) Validate(_ *pop.Connection) (*validate.Error...
  function createTemplate (line 31) | func createTemplate(serviceName string, creationTime time.Time) *x509.Ce...
  function GenerateCertificate (line 45) | func GenerateCertificate(serviceName string, privateKey *rsa.PrivateKey,...
  function encryptPrivateKey (line 60) | func encryptPrivateKey(privateKey []byte, encryptionKey string) (string,...
  function NewSamlCertificate (line 70) | func NewSamlCertificate(serviceName string) (*SamlCertificate, error) {

FILE: backend/persistence/models/saml_identity.go
  type SamlIdentity (line 11) | type SamlIdentity struct
    method Validate (line 21) | func (i *SamlIdentity) Validate(tx *pop.Connection) (*validate.Errors,...
  type SamlIdentities (line 19) | type SamlIdentities

FILE: backend/persistence/models/saml_idp_initiated_request.go
  type SamlIDPInitiatedRequest (line 11) | type SamlIDPInitiatedRequest struct
    method TableName (line 31) | func (samlIDPInitiatedRequest SamlIDPInitiatedRequest) TableName() str...
    method Validate (line 35) | func (r *SamlIDPInitiatedRequest) Validate(tx *pop.Connection) (*valid...
  function NewSamlIDPInitiatedRequest (line 19) | func NewSamlIDPInitiatedRequest(responseID, issuer string, expiresAt tim...

FILE: backend/persistence/models/saml_state.go
  type SamlState (line 11) | type SamlState struct
  function NewSamlState (line 20) | func NewSamlState(nonce string, state string) (*SamlState, error) {

FILE: backend/persistence/models/session.go
  type Session (line 12) | type Session struct
    method Validate (line 23) | func (session *Session) Validate(tx *pop.Connection) (*validate.Errors...

FILE: backend/persistence/models/token.go
  type Token (line 15) | type Token struct
    method Validate (line 92) | func (token *Token) Validate(tx *pop.Connection) (*validate.Errors, er...
  function TokenWithIdentityID (line 29) | func TokenWithIdentityID(identityID uuid.UUID) func(*Token) {
  function TokenForFlowAPI (line 35) | func TokenForFlowAPI(isFlow bool) func(*Token) {
  function TokenUserCreated (line 41) | func TokenUserCreated(userCreated bool) func(*Token) {
  function TokenPKCESessionVerifier (line 47) | func TokenPKCESessionVerifier(pkceSessionVerifier string) func(*Token) {
  function TokenWithLinkUser (line 53) | func TokenWithLinkUser(linkUser bool) func(*Token) {
  function NewToken (line 59) | func NewToken(userID uuid.UUID, options ...func(*Token)) (*Token, error) {

FILE: backend/persistence/models/trusted_device.go
  type TrustedDevice (line 11) | type TrustedDevice struct
    method Validate (line 20) | func (trustedDevice *TrustedDevice) Validate(tx *pop.Connection) (*val...

FILE: backend/persistence/models/user.go
  type ProviderProfile (line 20) | type ProviderProfile struct
  type User (line 28) | type User struct
    method DeleteWebauthnCredential (line 45) | func (user *User) DeleteWebauthnCredential(credentialId string) {
    method GetIdentities (line 54) | func (user *User) GetIdentities() Identities {
    method GetUsername (line 71) | func (user *User) GetUsername() *string {
    method SetUsername (line 78) | func (user *User) SetUsername(username *Username) {
    method DeleteUsername (line 82) | func (user *User) DeleteUsername() {
    method SetPrimaryEmail (line 86) | func (user *User) SetPrimaryEmail(primary *PrimaryEmail) {
    method UpdateEmail (line 96) | func (user *User) UpdateEmail(email Email) {
    method DeleteEmail (line 105) | func (user *User) DeleteEmail(email Email) {
    method DeleteOTPSecret (line 114) | func (user *User) DeleteOTPSecret() {
    method GetEmailById (line 118) | func (user *User) GetEmailById(emailId uuid.UUID) *Email {
    method GetEmailByAddress (line 122) | func (user *User) GetEmailByAddress(address string) *Email {
    method GetWebauthnCredentialById (line 126) | func (user *User) GetWebauthnCredentialById(credentialId string) *Weba...
    method GetPasskeys (line 135) | func (user *User) GetPasskeys() WebauthnCredentials {
    method GetSecurityKeys (line 145) | func (user *User) GetSecurityKeys() WebauthnCredentials {
    method Validate (line 156) | func (user *User) Validate(tx *pop.Connection) (*validate.Errors, erro...
    method WebAuthnID (line 164) | func (user *User) WebAuthnID() []byte {
    method WebAuthnName (line 168) | func (user *User) WebAuthnName() string {
    method WebAuthnDisplayName (line 176) | func (user *User) WebAuthnDisplayName() string {
    method WebAuthnIcon (line 184) | func (user *User) WebAuthnIcon() string {
    method WebAuthnCredentials (line 188) | func (user *User) WebAuthnCredentials() []webauthn.Credential {
    method SyncFromProviderProfile (line 220) | func (user *User) SyncFromProviderProfile(profile ProviderProfile) (ch...
  function NewUser (line 62) | func NewUser() User {

FILE: backend/persistence/models/user_metadata.go
  type UserMetadata (line 13) | type UserMetadata struct
    method Validate (line 23) | func (m *UserMetadata) Validate(tx *pop.Connection) (*validate.Errors,...

FILE: backend/persistence/models/username.go
  type Username (line 11) | type Username struct
    method Validate (line 30) | func (username *Username) Validate(tx *pop.Connection) (*validate.Erro...
  function NewUsername (line 19) | func NewUsername(userId uuid.UUID, username string) *Username {

FILE: backend/persistence/models/webauthn_credential.go
  type WebauthnCredential (line 15) | type WebauthnCredential struct
    method Validate (line 37) | func (credential *WebauthnCredential) Validate(tx *pop.Connection) (*v...
    method GetWebauthnTransports (line 48) | func (credential *WebauthnCredential) GetWebauthnTransports() []protoc...
    method GetWebauthnDescriptor (line 56) | func (credential *WebauthnCredential) GetWebauthnDescriptor() (*protoc...
  type WebauthnCredentials (line 34) | type WebauthnCredentials
    method GetWebauthnDescriptors (line 71) | func (credentials WebauthnCredentials) GetWebauthnDescriptors() ([]pro...

FILE: backend/persistence/models/webauthn_credential_transport.go
  type WebauthnCredentialTransport (line 11) | type WebauthnCredentialTransport struct
    method Validate (line 29) | func (transport *WebauthnCredentialTransport) Validate(tx *pop.Connect...
  type Transports (line 18) | type Transports
    method GetNames (line 20) | func (transports Transports) GetNames() []string {

FILE: backend/persistence/models/webauthn_credential_user_handle.go
  type WebauthnCredentialUserHandle (line 11) | type WebauthnCredentialUserHandle struct
    method Validate (line 20) | func (userHandle *WebauthnCredentialUserHandle) Validate(tx *pop.Conne...

FILE: backend/persistence/models/webauthn_session_data.go
  type Operation (line 16) | type Operation
  type WebauthnSessionData (line 24) | type WebauthnSessionData struct
    method decodeAllowedCredentials (line 36) | func (sd *WebauthnSessionData) decodeAllowedCredentials() [][]byte {
    method ToSessionData (line 95) | func (sd *WebauthnSessionData) ToSessionData() *webauthn.SessionData {
    method Validate (line 117) | func (sd *WebauthnSessionData) Validate(tx *pop.Connection) (*validate...
  function NewWebauthnSessionDataFrom (line 51) | func NewWebauthnSessionDataFrom(sessionData *webauthn.SessionData, opera...

FILE: backend/persistence/models/webauthn_session_data_allowed_credential.go
  type WebauthnSessio
Condensed preview — 998 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,745K chars).
[
  {
    "path": ".editorconfig",
    "chars": 242,
    "preview": "[**]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[**.{ts,tsx,json,js,c"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/BUG_REPORT.yml",
    "chars": 3689,
    "preview": "description: \"Create a bug report\"\nlabels:\n  - bug\nname: \"Bug Report\"\nbody:\n  - attributes:\n      value: \"Thank you for "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml",
    "chars": 1793,
    "preview": "description:\n  \"Make a feature request\"\nlabels:\n  - enhancement\nname: \"Feature Request\"\nbody:\n  - attributes:\n      valu"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 278,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Hanko Discussions\n    url: https://github.com/teamhanko/hanko/discu"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1197,
    "preview": "<!-- Thank you for submitting a pull request for this project! This is a pull request template,\nplease remove any sectio"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 922,
    "preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
  },
  {
    "path": ".github/workflows/build-frontend.yml",
    "chars": 558,
    "preview": "name: build-frontend\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  tests:\n    runs"
  },
  {
    "path": ".github/workflows/cli-publish.yml",
    "chars": 891,
    "preview": "name: CLI\n\non:\n  release:\n    types: [published]\n\npermissions:\n  contents: write\n\njobs:\n  goreleaser:\n    runs-on: ubunt"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2727,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".github/workflows/docker-publish.yml",
    "chars": 2684,
    "preview": "name: Docker\n\n# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and ar"
  },
  {
    "path": ".github/workflows/e2e.yml",
    "chars": 2202,
    "preview": "name: E2E Test\n\non:\n  workflow_dispatch:\n\njobs:\n  passwordless:\n    runs-on: ubuntu-22.04\n    steps:\n      - name: Check"
  },
  {
    "path": ".github/workflows/go.yml",
    "chars": 465,
    "preview": "name: Go\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n\n  build:\n    runs-on: ubuntu"
  },
  {
    "path": ".github/workflows/release-frontend-sdk.yml",
    "chars": 2091,
    "preview": "name: Release @teamhanko/frontend-sdk\n\non:\n  push:\n    tags:\n      - '@teamhanko/frontend-sdk@*'\n\ndefaults:\n  run:\n    w"
  },
  {
    "path": ".github/workflows/release-hanko-elements.yml",
    "chars": 1549,
    "preview": "name: Release @teamhanko/hanko-elements\n\non:\n  push:\n    tags:\n      - '@teamhanko/hanko-elements@*'\n\ndefaults:\n  run:\n "
  },
  {
    "path": ".github/workflows/schema-generate-config.yml",
    "chars": 1763,
    "preview": "name: Generate config JSON schema\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - 'backend/config/*.go'\n     "
  },
  {
    "path": ".github/workflows/schema-generate-import.yml",
    "chars": 1687,
    "preview": "name: Generate import JSON schema\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - backend/cmd/user/format.go\n"
  },
  {
    "path": ".github/workflows/schema-markdown-config.yml",
    "chars": 2255,
    "preview": "name: Generate config reference markdown\n\non:\n  push:\n    tags:\n      - 'backend/*'\n  workflow_dispatch:\n\njobs:\n  config"
  },
  {
    "path": ".github/workflows/schema-markdown-import.yml",
    "chars": 3062,
    "preview": "name: Generate user import reference markdown\n\non:\n  push:\n    tags:\n      - 'backend/*'\n  workflow_dispatch:\n\njobs:\n  i"
  },
  {
    "path": ".gitignore",
    "chars": 489,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n#env files\n*.env\n\n# Test binary, built with `go tes"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5214,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 7754,
    "preview": "# Contributing to Hanko\n\nThank you for considering contributing to Hanko! Following are the guidelines we would like you"
  },
  {
    "path": "LICENSE",
    "chars": 35289,
    "preview": "  Portions of this software are licensed as follows:\n\n  All content that resides under the \"elements\" directory\n(https:/"
  },
  {
    "path": "README.md",
    "chars": 6995,
    "preview": "<p align=\"center\">\n  <img width=\"300\" src=\"https://user-images.githubusercontent.com/20115649/176922807-fb92327a-15d5-45"
  },
  {
    "path": "SECURITY.md",
    "chars": 488,
    "preview": "# Security Policy\n\n## Reporting a Vulnerability\n\nIf you discover a security vulnerability, we appreciate any information"
  },
  {
    "path": "backend/.goreleaser.yaml",
    "chars": 473,
    "preview": "before:\n  hooks:\n    - go mod tidy\n    - go generate ./...\nbuilds:\n  - env:\n      - CGO_ENABLED=0\n    goos:\n      - linu"
  },
  {
    "path": "backend/Dockerfile",
    "chars": 1092,
    "preview": "# Build the hanko binary\nFROM --platform=$BUILDPLATFORM golang:1.26 AS builder\n\nARG TARGETARCH\n\nWORKDIR /workspace\nCOPY "
  },
  {
    "path": "backend/Dockerfile.debug",
    "chars": 1350,
    "preview": "# Build the hanko binary\nFROM golang:1.26 AS builder\nWORKDIR /workspace\n\n# Get Delve\nRUN CGO_ENABLED=0 GOOS=linux GOARCH"
  },
  {
    "path": "backend/README.md",
    "chars": 31706,
    "preview": "# Hanko backend\n\nHanko backend provides an HTTP API to build a modern login and registration experience for your users. "
  },
  {
    "path": "backend/audit_log/logger.go",
    "chars": 4120,
    "preview": "package auditlog\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/labstack/echo/v4\"\n\tzeroLog \"github.com/rs/zerolog"
  },
  {
    "path": "backend/build_info/build_info.go",
    "chars": 761,
    "preview": "package build_info\n\nimport (\n\t_ \"embed\"\n\t\"runtime/debug\"\n\t\"strings\"\n)\n\n//go:generate sh -c \"git describe --tags --always"
  },
  {
    "path": "backend/cmd/cleanup/cleanup.go",
    "chars": 5321,
    "preview": "package cleanup\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/"
  },
  {
    "path": "backend/cmd/isready/isready.go",
    "chars": 1703,
    "preview": "package isready\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"log\"\n\t\"net\""
  },
  {
    "path": "backend/cmd/jwk/create.go",
    "chars": 639,
    "preview": "package jwk\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/crypto/"
  },
  {
    "path": "backend/cmd/jwk/root.go",
    "chars": 327,
    "preview": "package jwk\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\n\nfunc NewMigrateCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:"
  },
  {
    "path": "backend/cmd/jwt/create.go",
    "chars": 2913,
    "preview": "package jwt\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/spf13/c"
  },
  {
    "path": "backend/cmd/jwt/root.go",
    "chars": 320,
    "preview": "package jwt\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NewJwtCmd() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse:   \"j"
  },
  {
    "path": "backend/cmd/migrate/down.go",
    "chars": 1022,
    "preview": "package migrate\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/"
  },
  {
    "path": "backend/cmd/migrate/root.go",
    "chars": 470,
    "preview": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n\n*/\n\npackage migrate\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\n//var p"
  },
  {
    "path": "backend/cmd/migrate/up.go",
    "chars": 917,
    "preview": "package migrate\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/"
  },
  {
    "path": "backend/cmd/root.go",
    "chars": 1279,
    "preview": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage cmd\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/tea"
  },
  {
    "path": "backend/cmd/schema/generate.go",
    "chars": 2177,
    "preview": "package schema\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/invopop/jsonschema\"\n\t\"github.com/spf13/cobra\"\n\t\""
  },
  {
    "path": "backend/cmd/schema/generate_config.go",
    "chars": 759,
    "preview": "package schema\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"log\"\n)\n\nfunc NewGen"
  },
  {
    "path": "backend/cmd/schema/generate_import.go",
    "chars": 781,
    "preview": "package schema\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/user\"\n\t\"log\"\n)\n\nfunc NewG"
  },
  {
    "path": "backend/cmd/schema/markdown.go",
    "chars": 304,
    "preview": "package schema\n\nimport \"github.com/spf13/cobra\"\n\nfunc NewMarkdownCommand() *cobra.Command {\n\tcmd := &cobra.Command{\n\t\tUs"
  },
  {
    "path": "backend/cmd/schema/markdown_config.go",
    "chars": 1362,
    "preview": "package schema\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"log\"\n\t\"os/ex"
  },
  {
    "path": "backend/cmd/schema/markdown_import.go",
    "chars": 1384,
    "preview": "package schema\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/cmd/user\"\n\t\"log\"\n\t\"os/"
  },
  {
    "path": "backend/cmd/schema/root.go",
    "chars": 373,
    "preview": "package schema\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NewSchemaCommand() *cobra.Command {\n\treturn &cobra.Command{\n\t"
  },
  {
    "path": "backend/cmd/serve/admin.go",
    "chars": 905,
    "preview": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage serve\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/t"
  },
  {
    "path": "backend/cmd/serve/all.go",
    "chars": 1394,
    "preview": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage serve\n\nimport (\n\t\"github.com/labstack/echo-contrib/echop"
  },
  {
    "path": "backend/cmd/serve/public.go",
    "chars": 1219,
    "preview": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n*/\npackage serve\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"github.com/t"
  },
  {
    "path": "backend/cmd/serve/root.go",
    "chars": 464,
    "preview": "/*\nCopyright © 2022 Hanko GmbH <developers@hanko.io>\n\n*/\npackage serve\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NewSe"
  },
  {
    "path": "backend/cmd/siwa/siwa.go",
    "chars": 2160,
    "preview": "package siwa\n\nimport (\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-"
  },
  {
    "path": "backend/cmd/user/export.go",
    "chars": 1911,
    "preview": "package user\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/back"
  },
  {
    "path": "backend/cmd/user/format.go",
    "chars": 7610,
    "preview": "package user\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/"
  },
  {
    "path": "backend/cmd/user/format_test.go",
    "chars": 7447,
    "preview": "package user\n\nimport (\n\t\"fmt\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/gofrs/uuid\"\n\t\"testing\"\n\t\"time\"\n\n\t\"g"
  },
  {
    "path": "backend/cmd/user/generate.go",
    "chars": 1355,
    "preview": "package user\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/brianvoe/gofakeit/v6\"\n\t\"github.com/gofrs/uuid"
  },
  {
    "path": "backend/cmd/user/import.go",
    "chars": 5894,
    "preview": "package user\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/teamhank"
  },
  {
    "path": "backend/cmd/user/import_test.go",
    "chars": 5367,
    "preview": "package user\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/stretchr"
  },
  {
    "path": "backend/cmd/user/importer.go",
    "chars": 6027,
    "preview": "package user\n\nimport (\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/p"
  },
  {
    "path": "backend/cmd/user/root.go",
    "chars": 484,
    "preview": "package user\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NewUserCommand() *cobra.Command {\n\treturn &cobra.Command{\n\t\tUse"
  },
  {
    "path": "backend/cmd/version/version.go",
    "chars": 776,
    "preview": "package version\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/teamhanko/hanko/backend/v2/build_info\"\n)\n\nfunc N"
  },
  {
    "path": "backend/config/config.go",
    "chars": 10360,
    "preview": "package config\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/kelseyhightower/envconfig\"\n\t\"github.com/knadh/koanf/parsers/yaml\"\n\t"
  },
  {
    "path": "backend/config/config.yaml",
    "chars": 3144,
    "preview": "audit_log:\n  storage:\n    enabled: false\n  retention: 720h\naccount:\n  allow_deletion: true\n  allow_signup: true\nconvert_"
  },
  {
    "path": "backend/config/config_account.go",
    "chars": 433,
    "preview": "package config\n\ntype Account struct {\n\t// `allow_deletion` determines whether users can delete their accounts.\n\tAllowDel"
  },
  {
    "path": "backend/config/config_audit_log.go",
    "chars": 1854,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\ntype AuditLog struct {\n\t// `console_output` controls audit log console out"
  },
  {
    "path": "backend/config/config_database.go",
    "chars": 2101,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\ntype Database struct {\n\t// `database` determines the name of the databa"
  },
  {
    "path": "backend/config/config_default.go",
    "chars": 6077,
    "preview": "package config\n\nimport \"time\"\n\nfunc DefaultConfig() *Config {\n\treturn &Config{\n\t\tConvertLegacyConfig:                  f"
  },
  {
    "path": "backend/config/config_email.go",
    "chars": 3454,
    "preview": "package config\n\nimport \"fmt\"\n\ntype Email struct {\n\t// `acquire_on_login` determines whether users, provided that they do"
  },
  {
    "path": "backend/config/config_email_delivery.go",
    "chars": 1561,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\ntype EmailDelivery struct {\n\t// `enabled` determines whether the API de"
  },
  {
    "path": "backend/config/config_emails.go",
    "chars": 470,
    "preview": "package config\n\ntype Emails struct {\n\t// Deprecated. Use `email.require_verification` instead.\n\tRequireVerification bool"
  },
  {
    "path": "backend/config/config_flow_locker.go",
    "chars": 1466,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\ntype FlowLocker struct {\n\t// `enabled` controls whether flow locking is en"
  },
  {
    "path": "backend/config/config_logger.go",
    "chars": 311,
    "preview": "package config\n\ntype LoggerConfig struct {\n\t// `log_health_and_metrics` determines whether requests of the `/health` and"
  },
  {
    "path": "backend/config/config_mfa.go",
    "chars": 5207,
    "preview": "package config\n\nimport (\n\t\"github.com/invopop/jsonschema\"\n\t\"time\"\n)\n\ntype SecurityKeys struct {\n\t// `attestation_prefere"
  },
  {
    "path": "backend/config/config_passcode.go",
    "chars": 171,
    "preview": "package config\n\ntype Passcode struct {\n\t// Deprecated. Use `email.passcode_ttl` instead.\n\tTTL int `yaml:\"ttl\" json:\"ttl,"
  },
  {
    "path": "backend/config/config_passkey.go",
    "chars": 4875,
    "preview": "package config\n\nimport \"github.com/invopop/jsonschema\"\n\ntype Passkey struct {\n\t// `acquire_on_registration` configures h"
  },
  {
    "path": "backend/config/config_password.go",
    "chars": 3373,
    "preview": "package config\n\nimport \"github.com/invopop/jsonschema\"\n\ntype Password struct {\n\t// `acquire_on_registration` configures "
  },
  {
    "path": "backend/config/config_privacy.go",
    "chars": 944,
    "preview": "package config\n\ntype Privacy struct {\n\t// `show_account_existence_hints` determines whether the user should get a user-f"
  },
  {
    "path": "backend/config/config_rate_limiter.go",
    "chars": 2890,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\ntype RateLimiter struct {\n\t// `enabled` controls whether rate limiting is "
  },
  {
    "path": "backend/config/config_secrets.go",
    "chars": 4852,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/invopop/jsonschema\"\n\torderedmap \"github.com/wk8/go-ordered-map/v"
  },
  {
    "path": "backend/config/config_security_notifications.go",
    "chars": 1279,
    "preview": "package config\n\ntype SecurityNotifications struct {\n\tNotifications SecurityNotificationTypes `yaml:\"notifications\" json:"
  },
  {
    "path": "backend/config/config_server.go",
    "chars": 3026,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Server struct {\n\t// `public` contains the server configurat"
  },
  {
    "path": "backend/config/config_service.go",
    "chars": 402,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\ntype Service struct {\n\t// `name` determines the name of the service.\n\t/"
  },
  {
    "path": "backend/config/config_session.go",
    "chars": 8653,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/invopop/jsonschema\"\n)\n\ntype Session struct {\n\t// `allow_revocat"
  },
  {
    "path": "backend/config/config_shared.go",
    "chars": 665,
    "preview": "package config\n\nimport \"github.com/invopop/jsonschema\"\n\ntype RedisConfig struct {\n\t// `address` is the address of the re"
  },
  {
    "path": "backend/config/config_test.go",
    "chars": 4465,
    "preview": "package config\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testif"
  },
  {
    "path": "backend/config/config_third_party.go",
    "chars": 19927,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/fatih/structs\"\n\t\"github.com/gobwas/glob\"\n\t\"github.com"
  },
  {
    "path": "backend/config/config_username.go",
    "chars": 1777,
    "preview": "package config\n\ntype Username struct {\n\t// `acquire_on_login` determines whether users, provided that they do not alread"
  },
  {
    "path": "backend/config/config_webauthn.go",
    "chars": 4633,
    "preview": "package config\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\twebauthnLib \""
  },
  {
    "path": "backend/config/config_webhook.go",
    "chars": 4729,
    "preview": "package config\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/invopop/jsonschema\"\n\t\"github.com/teamhanko/hanko/backend/v"
  },
  {
    "path": "backend/config/config_webhook_test.go",
    "chars": 470,
    "preview": "package config\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestWebhooks_Decode(t *testing.T) {\n\tw"
  },
  {
    "path": "backend/config/minimal-config.yaml",
    "chars": 251,
    "preview": "smtp:\n    port: \"465\"\n    host: smtp.example.com\n    user: example\n    password: example\ndatabase:\n  url: postgres://pos"
  },
  {
    "path": "backend/config/passcode-smtp-config.yaml",
    "chars": 304,
    "preview": "database:\n  user: hanko\n  password: hanko\n  host: localhost\n  port: 5432\n  dialect: postgres\npasscode:\n  email:\n    from"
  },
  {
    "path": "backend/config/root-passcode-smtp-config.yaml",
    "chars": 398,
    "preview": "database:\n  user: hanko\n  password: hanko\n  host: localhost\n  port: 5432\n  dialect: postgres\nsmtp:\n    host: smtp1.examp"
  },
  {
    "path": "backend/config/security-notifications-disabled-config.yaml",
    "chars": 572,
    "preview": "smtp:\n    port: \"465\"\n    host: smtp.example.com\n    user: example\n    password: example\ndatabase:\n  url: postgres://pos"
  },
  {
    "path": "backend/crypto/aes_gcm/aes_gcm.go",
    "chars": 2390,
    "preview": "package aes_gcm\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\""
  },
  {
    "path": "backend/crypto/aes_gcm/aes_gcm_test.go",
    "chars": 3301,
    "preview": "package aes_gcm\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n)\nimport \"github.com/stretchr/testify/assert\"\n\nfunc Tes"
  },
  {
    "path": "backend/crypto/jwk/aws_kms/adapter.go",
    "chars": 4275,
    "preview": "package aws_kms\n\nimport (\n\t\"context\"\n\t\"crypto\"\n\t\"crypto/rsa\"\n\t\"crypto/sha256\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"sync\""
  },
  {
    "path": "backend/crypto/jwk/aws_kms/manager.go",
    "chars": 2463,
    "preview": "package aws_kms\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v"
  },
  {
    "path": "backend/crypto/jwk/local_db/generator.go",
    "chars": 225,
    "preview": "package local_db\n\nimport \"github.com/lestrrat-go/jwx/v2/jwk\"\n\n// KeyGenerator Interface for JSON Web Key Generation\ntype"
  },
  {
    "path": "backend/crypto/jwk/local_db/generator_rsa.go",
    "chars": 743,
    "preview": "package local_db\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/j"
  },
  {
    "path": "backend/crypto/jwk/local_db/generator_test.go",
    "chars": 979,
    "preview": "package local_db\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/"
  },
  {
    "path": "backend/crypto/jwk/local_db/manager.go",
    "chars": 3681,
    "preview": "package local_db\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwa"
  },
  {
    "path": "backend/crypto/jwk/local_db/manager_test.go",
    "chars": 1320,
    "preview": "package local_db\n\nimport (\n\t\"github.com/lestrrat-go/jwx/v2/jwa\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"github.com/stretc"
  },
  {
    "path": "backend/crypto/jwk/manager.go",
    "chars": 633,
    "preview": "package jwk\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/cr"
  },
  {
    "path": "backend/crypto/jwk/types.go",
    "chars": 617,
    "preview": "package jwk\n\nimport (\n\t\"github.com/lestrrat-go/jwx/v2/jwk\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n)\n\n// KeyProvider combin"
  },
  {
    "path": "backend/crypto/passcode.go",
    "chars": 1213,
    "preview": "package crypto\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"math/big\"\n)\n\ntype PasscodeGenerator interface {\n\tGenerate() (string, er"
  },
  {
    "path": "backend/crypto/passcode_test.go",
    "chars": 1197,
    "preview": "package crypto\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPasscodeGenerator_Generate(t *tes"
  },
  {
    "path": "backend/crypto/string.go",
    "chars": 891,
    "preview": "package crypto\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n)\n\n// GenerateRandomBytes returns securely generated random b"
  },
  {
    "path": "backend/dto/admin/email.go",
    "chars": 1246,
    "preview": "package admin\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\n"
  },
  {
    "path": "backend/dto/admin/identity.go",
    "chars": 686,
    "preview": "package admin\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n)\n"
  },
  {
    "path": "backend/dto/admin/metadata.go",
    "chars": 1868,
    "preview": "package admin\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\""
  },
  {
    "path": "backend/dto/admin/otp.go",
    "chars": 249,
    "preview": "package admin\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype GetOTPRequestDto struct {\n\tUserID string `param:\"user_i"
  },
  {
    "path": "backend/dto/admin/password.go",
    "chars": 465,
    "preview": "package admin\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"time\"\n)\n\ntype PasswordCredential struct {\n\tID        uuid.UUID `json:"
  },
  {
    "path": "backend/dto/admin/session.go",
    "chars": 529,
    "preview": "package admin\n\ntype CreateSessionTokenDto struct {\n\tUserID    string `json:\"user_id\" validate:\"required,uuid\"`\n\tUserAgen"
  },
  {
    "path": "backend/dto/admin/user.go",
    "chars": 3646,
    "preview": "package admin\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/dto\"\n\t\"github.com/team"
  },
  {
    "path": "backend/dto/admin/username.go",
    "chars": 551,
    "preview": "package admin\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\n"
  },
  {
    "path": "backend/dto/admin/webauthn.go",
    "chars": 280,
    "preview": "package admin\n\ntype ListWebauthnCredentialsRequestDto struct {\n\tUserID string `param:\"user_id\" validate:\"required,uuid\"`"
  },
  {
    "path": "backend/dto/admin/webhook.go",
    "chars": 742,
    "preview": "package admin\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/persiste"
  },
  {
    "path": "backend/dto/config.go",
    "chars": 2132,
    "preview": "package dto\n\nimport (\n\t\"github.com/fatih/structs\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\tsamlConfig \"github.co"
  },
  {
    "path": "backend/dto/email.go",
    "chars": 1680,
    "preview": "package dto\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"githu"
  },
  {
    "path": "backend/dto/error_handler.go",
    "chars": 1172,
    "preview": "package dto\n\nimport (\n\t\"fmt\"\n\t\"github.com/labstack/echo/v4\"\n\t\"net/http\"\n)\n\nfunc ToHttpError(err error) *echo.HTTPError {"
  },
  {
    "path": "backend/dto/intern/WebauthnCredential.go",
    "chars": 2207,
    "preview": "package intern\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauth"
  },
  {
    "path": "backend/dto/intern/WebauthnSessionData.go",
    "chars": 2062,
    "preview": "package intern\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauth"
  },
  {
    "path": "backend/dto/intern/WebauthnUser.go",
    "chars": 1224,
    "preview": "package intern\n\nimport (\n\t\"errors\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/tea"
  },
  {
    "path": "backend/dto/metadata.go",
    "chars": 2931,
    "preview": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"github.com/tidwall/gjson\"\n\t\"strings\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/p"
  },
  {
    "path": "backend/dto/passcode.go",
    "chars": 435,
    "preview": "package dto\n\nimport \"time\"\n\ntype PasscodeFinishRequest struct {\n\tId   string `json:\"id\" validate:\"required,uuid4\"`\n\tCode"
  },
  {
    "path": "backend/dto/profile.go",
    "chars": 3084,
    "preview": "package dto\n\nimport (\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/tea"
  },
  {
    "path": "backend/dto/session.go",
    "chars": 4770,
    "preview": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\"\n\t\"g"
  },
  {
    "path": "backend/dto/session_test.go",
    "chars": 7099,
    "preview": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/lestrrat-go/jwx/v2/jwt\""
  },
  {
    "path": "backend/dto/thirdparty.go",
    "chars": 2211,
    "preview": "package dto\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fatih/structs\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/back"
  },
  {
    "path": "backend/dto/user.go",
    "chars": 2549,
    "preview": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persist"
  },
  {
    "path": "backend/dto/username.go",
    "chars": 504,
    "preview": "package dto\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\nty"
  },
  {
    "path": "backend/dto/validator.go",
    "chars": 2559,
    "preview": "package dto\n\nimport (\n\t\"fmt\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/labstack/echo/v4\"\n\t\"github.com/teamh"
  },
  {
    "path": "backend/dto/webauthn.go",
    "chars": 1347,
    "preview": "package dto\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/persistence/models\"\n\t\"time\"\n)\n\nty"
  },
  {
    "path": "backend/dto/webhook/email.go",
    "chars": 1248,
    "preview": "package webhook\n\ntype EmailSend struct {\n\tSubject          string `json:\"subject\"`        // subject\n\tBodyPlain        s"
  },
  {
    "path": "backend/ee/saml/config/saml.go",
    "chars": 9768,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gobwas/glob\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype Saml struct {\n\t// `ena"
  },
  {
    "path": "backend/ee/saml/config/saml_test.go",
    "chars": 6481,
    "preview": "package config\n\nimport (\n\t\"github.com/gobwas/glob\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestSamlConf"
  },
  {
    "path": "backend/ee/saml/dto/saml.go",
    "chars": 313,
    "preview": "package dto\n\ntype SamlRequest struct {\n\tDomain string `query:\"domain\" validate:\"required,fqdn\"`\n}\n\ntype SamlMetadataRequ"
  },
  {
    "path": "backend/ee/saml/handler.go",
    "chars": 13861,
    "preview": "package saml\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gobuffalo/pop/v6\"\n\t\"gith"
  },
  {
    "path": "backend/ee/saml/provider/auth0.go",
    "chars": 1360,
    "preview": "package provider\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\tsamlConfig \"github.com/teamhanko/hanko/backe"
  },
  {
    "path": "backend/ee/saml/provider/provider.go",
    "chars": 4176,
    "preview": "package provider\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\tsaml2 \"github.com/rus"
  },
  {
    "path": "backend/ee/saml/provider/saml.go",
    "chars": 5781,
    "preview": "package provider\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"github.com/fatih/structs\"\n\tsaml2 \"github.com/russellhaering/gosaml2\""
  },
  {
    "path": "backend/ee/saml/router.go",
    "chars": 586,
    "preview": "package saml\n\nimport (\n\t\"github.com/labstack/echo/v4\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"gith"
  },
  {
    "path": "backend/ee/saml/service.go",
    "chars": 3432,
    "preview": "package saml\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/teamhanko/hanko/backend/v2/ee"
  },
  {
    "path": "backend/ee/saml/state.go",
    "chars": 3677,
    "preview": "package saml\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github.com/te"
  },
  {
    "path": "backend/ee/saml/state_test.go",
    "chars": 5536,
    "preview": "package saml\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/stretch"
  },
  {
    "path": "backend/ee/saml/utils/response.go",
    "chars": 1671,
    "preview": "package utils\n\nimport (\n\t\"bytes\"\n\t\"compress/flate\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"github.com/beevik/etree\"\n\trtvalidator \"gi"
  },
  {
    "path": "backend/ee/saml/utils/url.go",
    "chars": 384,
    "preview": "package utils\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/ee/saml/config\"\n\t\"strings\"\n)\n\nfunc IsAllowedRedirect(con"
  },
  {
    "path": "backend/flow_api/flow/capabilities/action_send_capabilities.go",
    "chars": 1930,
    "preview": "package capabilities\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hank"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_continue_to_passkey.go",
    "chars": 625,
    "preview": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamh"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_continue_to_password.go",
    "chars": 624,
    "preview": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamh"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_register_password.go",
    "chars": 1967,
    "preview": "package credential_onboarding\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_skip_method_chooser.go",
    "chars": 1056,
    "preview": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamh"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_skip_passkey.go",
    "chars": 1883,
    "preview": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamh"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_skip_password.go",
    "chars": 1979,
    "preview": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamh"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_webauthn_generate_creation_options.go",
    "chars": 2277,
    "preview": "package credential_onboarding\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v"
  },
  {
    "path": "backend/flow_api/flow/credential_onboarding/action_webauthn_verify_attestation_response.go",
    "chars": 1152,
    "preview": "package credential_onboarding\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamh"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_continue_to_passcode_confirmation.go",
    "chars": 1560,
    "preview": "package credential_usage\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/tea"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_continue_to_passcode_confirmation_recovery.go",
    "chars": 1572,
    "preview": "package credential_usage\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/tea"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_continue_to_password_login.go",
    "chars": 821,
    "preview": "package credential_usage\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_continue_with_login_identifier.go",
    "chars": 11414,
    "preview": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\n\tauditlog \"github.com/teamhanko/hanko/backend/"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_password_login.go",
    "chars": 3728,
    "preview": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/back"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_password_recovery.go",
    "chars": 2774,
    "preview": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backe"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_remember_me.go",
    "chars": 1063,
    "preview": "package credential_usage\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/te"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_resend_passcode.go",
    "chars": 3519,
    "preview": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\t\"time\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/dto/webho"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_verify_passcode.go",
    "chars": 3738,
    "preview": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/back"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_webauthn_generate_request_options.go",
    "chars": 2471,
    "preview": "package credential_usage\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/action_webauthn_verify_assertion_response.go",
    "chars": 4420,
    "preview": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backe"
  },
  {
    "path": "backend/flow_api/flow/credential_usage/hook_send_passcode.go",
    "chars": 4077,
    "preview": "package credential_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\t\"time\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/h"
  },
  {
    "path": "backend/flow_api/flow/device_trust/action_trust_device.go",
    "chars": 747,
    "preview": "package device_trust\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhan"
  },
  {
    "path": "backend/flow_api/flow/device_trust/hook_issue_trust_device_cookie.go",
    "chars": 2825,
    "preview": "package device_trust\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/sha"
  },
  {
    "path": "backend/flow_api/flow/device_trust/hook_schedule_trust_device_state.go",
    "chars": 1134,
    "preview": "package device_trust\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\""
  },
  {
    "path": "backend/flow_api/flow/flows.go",
    "chars": 8553,
    "preview": "package flow\n\nimport (\n\t\"time\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/capabilities\"\n\t\"github.com/teamhan"
  },
  {
    "path": "backend/flow_api/flow/login/hook_create_email.go",
    "chars": 1615,
    "preview": "package login\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\""
  },
  {
    "path": "backend/flow_api/flow/login/hook_schedule_onboarding_states.go",
    "chars": 7282,
    "preview": "package login\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/device_tru"
  },
  {
    "path": "backend/flow_api/flow/login/hook_trigger_login_webhook.go",
    "chars": 618,
    "preview": "package login\n\nimport (\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github."
  },
  {
    "path": "backend/flow_api/flow/login/hook_webauthn_generate_request_options_cond.go",
    "chars": 1258,
    "preview": "package login\n\nimport (\n\t\"fmt\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hank"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/action_continue_to_otp_secret_creation.go",
    "chars": 731,
    "preview": "package mfa_creation\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hank"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/action_continue_to_security_key_creation.go",
    "chars": 1305,
    "preview": "package mfa_creation\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hank"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/action_otp_code_verify.go",
    "chars": 2731,
    "preview": "package mfa_creation\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pquerna/otp/totp\"\n\tauditlog \"github.com/tea"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/action_webauthn_generate_creation_options_for_security_keys.go",
    "chars": 2357,
    "preview": "package mfa_creation\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_ap"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/hook_otp_secret_generate.go",
    "chars": 2078,
    "preview": "package mfa_creation\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/pquerna/otp/totp\"\n\t\"github.com/"
  },
  {
    "path": "backend/flow_api/flow/mfa_creation/skip_mfa.go",
    "chars": 556,
    "preview": "package mfa_creation\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hank"
  },
  {
    "path": "backend/flow_api/flow/mfa_usage/action_continue_to_login_otp.go",
    "chars": 737,
    "preview": "package mfa_usage\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/b"
  },
  {
    "path": "backend/flow_api/flow/mfa_usage/action_continue_to_login_security_key.go",
    "chars": 936,
    "preview": "package mfa_usage\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/b"
  },
  {
    "path": "backend/flow_api/flow/mfa_usage/action_otp_code_validate.go",
    "chars": 2193,
    "preview": "package mfa_usage\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/pquerna/otp/totp\"\n\t\"github.com/teamha"
  },
  {
    "path": "backend/flow_api/flow/mfa_usage/action_webauthn_generate_request_options_security_key.go",
    "chars": 1956,
    "preview": "package mfa_usage\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared"
  },
  {
    "path": "backend/flow_api/flow/profile/action_account_delete.go",
    "chars": 1868,
    "preview": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hank"
  },
  {
    "path": "backend/flow_api/flow/profile/action_connect_thirdparty_oauth_provider.go",
    "chars": 4542,
    "preview": "package profile\n\nimport (\n\t\"cmp\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"slices\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/config\"\n\t\"github."
  },
  {
    "path": "backend/flow_api/flow/profile/action_continue_to_otp_secret_creation.go",
    "chars": 1271,
    "preview": "package profile\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/bac"
  },
  {
    "path": "backend/flow_api/flow/profile/action_continue_to_security_key_creation.go",
    "chars": 1801,
    "preview": "package profile\n\nimport (\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/hanko/bac"
  },
  {
    "path": "backend/flow_api/flow/profile/action_disconnect_thirdparty_oauth_provider.go",
    "chars": 2220,
    "preview": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/f"
  },
  {
    "path": "backend/flow_api/flow/profile/action_email_create.go",
    "chars": 5061,
    "preview": "package profile\n\nimport (\n\t\"fmt\"\n\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/han"
  },
  {
    "path": "backend/flow_api/flow/profile/action_email_delete.go",
    "chars": 4480,
    "preview": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/au"
  },
  {
    "path": "backend/flow_api/flow/profile/action_email_set_primary.go",
    "chars": 3758,
    "preview": "package profile\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/gofrs/uuid\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t"
  },
  {
    "path": "backend/flow_api/flow/profile/action_email_verify.go",
    "chars": 1889,
    "preview": "package profile\n\nimport (\n\t\"fmt\"\n\t\"github.com/gofrs/uuid\"\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n"
  },
  {
    "path": "backend/flow_api/flow/profile/action_exchange_token.go",
    "chars": 3324,
    "preview": "package profile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"gith"
  },
  {
    "path": "backend/flow_api/flow/profile/action_otp_secret_delete.go",
    "chars": 1743,
    "preview": "package profile\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/teamhanko/hanko/backend/v2/flow_api/flow/shared\"\n\t\"github.com/teamhanko/h"
  },
  {
    "path": "backend/flow_api/flow/profile/action_password_create.go",
    "chars": 1977,
    "preview": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hank"
  },
  {
    "path": "backend/flow_api/flow/profile/action_password_delete.go",
    "chars": 2848,
    "preview": "package profile\n\nimport (\n\t\"fmt\"\n\tauditlog \"github.com/teamhanko/hanko/backend/v2/audit_log\"\n\t\"github.com/teamhanko/hank"
  }
]

// ... and 798 more files (download for full content)

About this extraction

This page contains the full source code of the teamhanko/hanko GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 998 files (5.2 MB), approximately 1.4M tokens, and a symbol index with 3009 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!