Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f000c8d
Add support for multiple languages with configurable translations. Up…
Jan 6, 2025
c8ab955
Add support for multiple languages with configurable translations. Up…
Jan 6, 2025
116987a
Changed some Dutch comments to English
Jan 7, 2025
8a8e6b3
Enable or Disable registration via the option: enable_user_signup
Jan 7, 2025
9df010b
Remove local replace in go.mod
Jan 7, 2025
d38adca
remove go.mod.local
Jan 7, 2025
81acd85
some fixes
Apr 16, 2025
5e73b4a
Bugfixes in release 0.6.6
Apr 16, 2025
6147273
some changes in the README
Apr 16, 2025
8dea6cd
Added function with support for English, Portuguese, Spanish, Dutch,…
Apr 16, 2025
7c0682e
See Changelog
Apr 17, 2025
841e111
typo fix in dutch.sample
Apr 17, 2025
e5d9606
Update password translations in password.go
Oct 23, 2025
e9d21a7
Add embedded logo to emails (CID attachment)
Mar 25, 2026
f91d02c
Add embedded logo to emails (CID attachment)
Mar 25, 2026
786b031
Add embedded logo to emails (CID attachment)
Mar 25, 2026
3eda1e2
Delete create_templates.py
tubby1981 Mar 25, 2026
46870a2
Merge branch 'main' of https://github.com/tubby1981/mokey
Mar 25, 2026
9458fe7
Add account portal fields for department, job title, and phone numbers
Jun 3, 2026
6f3a31f
Update changelog for v0.6.10 release
Jun 3, 2026
7aa92cc
Release v0.6.11: expired password redirect, 408 pages, safer errors
Jun 4, 2026
5e271f1
Remove organization-specific branding from upstream defaults
Jun 4, 2026
71115df
Fix password reset from email links for v0.6.13.
Jun 8, 2026
98ecf2e
Update go.sum after vendoring goipa locally.
Jun 8, 2026
bb04dc1
Fix password reset login by preferring self-service password change.
Jun 8, 2026
68d9524
Fix password reset when pwpolicy lookup fails on FreeIPA.
Jun 8, 2026
5c3f6e1
Fix password expiration when IPA policy read is denied.
Jun 8, 2026
794e999
Enforce per-user IPA password policy and update v0.6.13 docs.
Jun 8, 2026
13c309b
fix: resolve login loop when password managers autofill credentials
Jun 30, 2026
055f802
fix: harden login flow for password managers
Jun 30, 2026
b5d92d9
fix: show forgot-password link on main login page
Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ dist/
key.gpg
mokey-*.tar.gz
*.swp
# Local full template override directories (brand-specific; not published upstream)
*-templates/
*-templates-*/
63 changes: 30 additions & 33 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,44 +1,36 @@
# Mokey goreleaser configs
# See here: https://goreleaser.com
version: 2
project_name: mokey

before:
hooks:
- go mod tidy

builds:
- env:
- id: mokey
env:
- CGO_ENABLED=1
goarch:
- amd64
goos:
- linux
ldflags:
- -s -w -X github.com/ubccr/mokey/server.Version={{.Version}}
- -s -w -X github.com/tubby1981/mokey/server.Version={{.Version}}
- -extldflags=-static
tags:
- sqlite_omit_load_extension
- osusergo
- netgo
archives:
- replacements:
linux: linux
amd64: x86_64
wrap_in_directory: true
name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}"
files:
- LICENSE
- NOTICE
- README.md
- ChangeLog.md
- mokey.toml.sample

nfpms:
- vendor: University at Buffalo
homepage: https://github.com/ubccr/mokey
maintainer: Andrew E. Bruno <aebruno2@buffalo.edu>
license: MIT
description: |-
FreeIPA self-service account management tool
- id: mokey
formats:
- deb
- rpm
maintainer: Andrew E. Bruno <aebruno2@buffalo.edu>
vendor: University at Buffalo
homepage: https://github.com/tubby1981/mokey
license: MIT
description: |
FreeIPA self-service account management tool
overrides:
deb:
file_name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Arch }}"
Expand All @@ -48,25 +40,30 @@ nfpms:
file_name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Arch }}"
scripts:
postinstall: ./scripts/nfpm/postinstall.sh
rpm:
signature:
key_file: key.gpg
deb:
signature:
key_file: key.gpg
#rpm:
# signature:
# key_file: key.gpg
#deb:
# signature:
# key_file: key.gpg
contents:
- src: ./scripts/nfpm/mokey.toml.default
dst: /etc/mokey/mokey.toml
type: "config|noreplace"
type: config|noreplace
- src: ./scripts/nfpm/mokey.env
dst: /etc/default/mokey
type: "config|noreplace"
- src: ./scripts/nfpm/translations/*
dst: /etc/mokey/translations/
file_info:
owner: mokey
group: mokey
- src: ./scripts/nfpm/mokey.service
dst: /usr/lib/systemd/system/mokey.service

checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-SNAPSHOT-{{.ShortCommit}}"
version_template: "{{ incpatch .Version }}-SNAPSHOT-{{.ShortCommit}}"
changelog:
sort: desc
groups:
Expand All @@ -81,4 +78,4 @@ changelog:
filters:
exclude:
- '^docs:'
- 'typo'
- 'typo'
69 changes: 69 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,74 @@
# Mokey ChangeLog

## [Unreleased]

## [v0.6.14] - 2026-06-30

### Login / password managers
- Fix login loop when browsers or password managers autofill credentials (HTMX now reads input values from the DOM)
- Use standard `autocomplete` attributes and input types on login and forgot-password forms
- Use readonly instead of disabled for the username field on the password step so password managers recognize the form
- Accept one-step login when password managers submit username and password together on `/auth/login`
- Route login forms through HTMX even when password managers call `form.submit()` directly
- Strip credentials from URL query strings on auth routes (prevents passwords in access logs)
- Accept CSRF token from hidden form field (not only `X-CSRF-Token` header)
- Add optional `server.auth_debug` logging for login flow troubleshooting
- Pass reset form path to password-reset template for HTMX form posts
- Show forgot-password link on the main login page

## [v0.6.13] - 2026-06-08

### Password reset
- Fix password reset from email links so users can log in immediately after choosing a new password
- Prefer `ResetPassword` + self-service `change_password` over admin `passwd` (admin-set passwords are marked expired by FreeIPA)
- Fall back to admin `passwd` when self-service reset fails; refresh `krbPasswordExpiration` from the user's effective password policy
- Use `pwpolicy_show` / `pwpolicy_find` for max lifetime, with `accounts.password_max_life_days` as config fallback
- Return accurate error messages on password reset (no longer always "Invalid OTP" when 2FA is disabled)
- Show the OTP field on password reset when the user has MFA enabled (`UserHasOTP`), not only for OTP-only accounts
- Add `password_reset.failed` translation for generic reset failures

### Password policy validation
- Enforce per-user FreeIPA password policy (min length, character classes) on password change, reset, and signup
- `min_passwd_len` and `min_passwd_classes` in `mokey.toml` are fallbacks when the service account cannot read IPA policies

### FreeIPA / goipa
- Vendor goipa locally with `AdminSetPassword`, `ResetUserPassword`, `PasswordPolicyForUser`, and omit empty OTP from `SetPassword` requests

### Other
- Add explicit `font-src 'self'` to Content-Security-Policy headers
- Update setup documentation: `Password Policy Readers` privilege, keytab verification, and config fallbacks

## [v0.6.12] - 2026-06-04

- Remove organization-specific strings from default translations and email logo defaults

## [v0.6.11] - 2026-06-04

- Redirect expired passwords to forgot-password flow (`/auth/forgotpw?expired=1`) instead of a dedicated expired-password page; remove `login-password-expired.html`
- Add HTTP 408 request timeout error pages (`408.html`, `408-partial.html`)
- Improve error handling: shared `renderErrorPage` with 500 fallback, fix error logger username context key
- Reduce noisy 502/error logs for benign client disconnects (`errors.go`)
- Make favicon middleware optional when `site.favicon` or bundled `images/favicon.ico` is missing
- Extract `userHasOTP` template helper to `userauth.go`
- Add `email.logo_path` to `mokey.toml.sample`
- Add translations for forgot-password expired redirect message
- Use generic default email branding paths and translation strings (no organization-specific names in upstream)

## [v0.6.10] - 2026-06-03

- Add account portal fields: Department (read-only), Job Title, Telephone Number (read-only), and Mobile Telephone Number (editable)
- Use tubby1981/goipa v0.0.8 for Org Unit and Job Title support in FreeIPA
- Ship translation files as dutch.toml and english.toml in packages
- Remove unused create_templates.py script

## [v0.6.7] - 2025-04-17
- Added `timeago` function with support for English, Portuguese, Spanish, Dutch, French, German, and Turkish. Based on [xeonx/timeago](https://github.com/xeonx/timeago).
- Some translations in the email templates were not working properly. This has been fixed.

## [v0.6.6] - 2025-04-16
- Add config option to hide registratrion link: enable_user_signup true/false
- Add support for multiple languages with configurable translations. See README

## [v0.6.5] - 2024-10-28

- Update fiber, htmx (v2.0.3), hyperscript (v0.9.13)
Expand Down
108 changes: 93 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Note: mokey needs to be installed on a machine already enrolled in FreeIPA.
It's also recommended to have the ipa-admintools package installed. Enrolling a
host in FreeIPA is outside the scope of this document.

To install mokey download a copy of the pre-compiled binary [here](https://github.com/ubccr/mokey/releases).
To install mokey download a copy of the pre-compiled binary [here](https://github.com/tubby1981/mokey/releases).

tar.gz archive:

Expand All @@ -61,17 +61,40 @@ $ sudo rpm -ivh mokey-VERSION-amd64.rpm

## Setup and configuration

Create a user account and role in FreeIPA with the "Modify users and Reset
passwords" privilege. This user account will be used by the mokey application
to reset users passwords. The "Modify Users" permission also needs to have the
"ipauserauthtype" enabled. Run the following commands (requires ipa-admintools
to be installed):
Create a dedicated service account in FreeIPA for mokey. All FreeIPA API calls
run as this account using a keytab (`site.ktuser` / `site.keytab` in
`mokey.toml`).

### Required FreeIPA privileges

The service account needs privileges to manage users and passwords. The
`User Administrators` privilege covers most day-to-day operations (user lookup,
password reset, MFA, SSH keys, and so on).

For password reset via email, mokey also needs to read password policies so it
can set `krbPasswordExpiration` correctly after an admin password change. Add
the **Password Policy Readers** privilege. Without it, `ipa pwpolicy-show`
fails for the service account and mokey falls back to
`accounts.password_max_life_days` in `mokey.toml`.

| Privilege | Purpose |
|-----------|---------|
| User Administrators | User management, password reset, MFA, SSH keys |
| Password Policy Readers | Read effective password policy (`maxlife`) per user |

The **System: Modify Users** permission must include the `ipauserauthtype`
attribute (required for MFA enable/disable).

### FreeIPA setup

Run the following on an IPA server (requires `ipa-admintools`):

```
$ mkdir /etc/mokey/private
$ kinit adminuser
$ ipa role-add 'Mokey User Manager' --desc='Mokey User management'
$ ipa role-add-privilege 'Mokey User Manager' --privilege='User Administrators'
$ ipa role-add 'Mokey User Manager' --desc='Mokey user management'
$ ipa role-add-privilege 'Mokey User Manager' --privileges='User Administrators'
$ ipa role-add-privilege 'Mokey User Manager' --privileges='Password Policy Readers'
$ ipa user-add mokeyapp --first Mokey --last App
$ ipa role-add-member 'Mokey User Manager' --users=mokeyapp
$ ipa permission-mod 'System: Modify Users' --includedattrs=ipauserauthtype
Expand All @@ -80,22 +103,50 @@ $ chmod 640 /etc/mokey/private/mokeyapp.keytab
$ chgrp mokey /etc/mokey/private/mokeyapp.keytab
```

Edit mokey configuration file and set path to keytab file. The values for
`token_secret` and `csrf_secret` will be automatically generated for you if
left blank. Set these secret values if you'd like sessions to persist after a restart.
For other site specific config options [see here](https://github.com/ubccr/mokey/blob/main/mokey.toml.sample):
Verify the service account can read password policies:

```
$ kinit -kt /etc/mokey/private/mokeyapp.keytab mokeyapp
$ ipa pwpolicy-show --user=someuser
$ kdestroy
```

If `pwpolicy-show` returns *password policy not found*, the role is missing
**Password Policy Readers** or the user is not a member of the role. As a
fallback, set `password_max_life_days` in `mokey.toml` to match your IPA policy
(for example `183`).

### mokey configuration

Edit `/etc/mokey/mokey.toml`. Set the keytab path and service account name. The
values for `token_secret` and `csrf_secret` are generated automatically if left
blank. Set them explicitly if you want sessions and tokens to persist across
restarts. For all options see
[mokey.toml.sample](https://github.com/tubby1981/mokey/blob/main/mokey.toml.sample).

```
$ vim /etc/mokey/mokey.toml
# Path to keytab file

[site]
# User account for the mokey service (must match keytab principal)
ktuser = "mokeyapp"
keytab = "/etc/mokey/private/mokeyapp.keytab"

# Secret key for branca tokens. Must be 32 bytes. To generate run:
# openssl rand -hex 32
# openssl rand -hex 32
token_secret = ""

# CSRF token secret key. Should be a random string
csrf_secret = ""

[accounts]
# Fallback when pwpolicy_show is unavailable to the service account.
# Per-user IPA policy is used automatically when mokeyapp can read it.
password_max_life_days = 183

# Fallback password validation limits when IPA policy cannot be read.
# min_passwd_len and min_passwd_classes in mokey.toml are used only as fallback;
# effective Min length and Character classes come from each user's IPA policy.
```

It's highly recommended to run mokey using HTTPS. You'll need an SSL
Expand Down Expand Up @@ -139,12 +190,39 @@ Any OAuth clients configured in Hydra will be authenticated via mokey using
FreeIPA as the identity provider. For an example OAuth 2.0/OIDC client
application see [here](examples/mokey-oidc/main.go).

## Translations

mokey supports multiple languages for its interface and email templates. Default are English and Dutch supported.

### Configuring Translations

1. **Place translation files**
Translation files should be placed in `/etc/mokey/translations/`.
For example:
- `english.toml` for English translations
- `dutch.toml` for Dutch translations

2. **Update the configuration file**
Add the following options to `/etc/mokey/mokey.toml`:
```toml
# Default language for the site
# Languages supported: English (english), Dutch (dutch)
# Default is english
default_language = "english"

# Directory where translations can be placed
translations_dir = "/etc/mokey/translations"
```

3. **Create custom translations**
Users can translate mokey into their own language by creating a new .toml file in the translations_dir and referencing it in the default_language configuration. This allows for complete customization of the interface and email templates in the preferred language.

## Building from source

First, you will need Go v1.21 or greater. Clone the repository:

```
$ git clone https://github.com/ubccr/mokey
$ git clone https://github.com/tubby1981/mokey
$ cd mokey
$ go build .
```
Expand Down
Binary file added a.out
Binary file not shown.
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/ubccr/mokey/server"
"github.com/tubby1981/mokey/server"
)

var (
Expand Down
4 changes: 2 additions & 2 deletions cmd/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/ubccr/mokey/cmd"
"github.com/ubccr/mokey/server"
"github.com/tubby1981/mokey/cmd"
"github.com/tubby1981/mokey/server"
)

var (
Expand Down
Loading