Repository navigation
Ops Github Login
Users can link their GitHub account in their settings and then sign in with it. Only linked accounts can sign in (nothing is matched by e-mail address, nobody registers through GitHub), and the password always stays. Design: ADR 0051.
The GitHub configuration lives only on the server: environment variables and a secret file. The web app never shows it; it only learns that GitHub is on (GET /api/v1/identity/login-methods answers "github": true).
On GitHub: Settings, Developer settings, OAuth Apps, New OAuth App (for an organization: the organization's settings, same path). Fields:
| Field | Value |
|---|---|
| Application name |
MowDeck (users see it on GitHub's consent page) |
| Homepage URL | the address users open MowDeck at, e.g. https://mowdeck.lukas2311-homelab.com
|
| Application description | optional |
| Authorization callback URL |
<IDENTITY_PUBLIC_URL>/api/v1/identity/oauth/github/callback, e.g. https://mowdeck.lukas2311-homelab.com/api/v1/identity/oauth/github/callback
|
| Enable Device Flow | off |
After Register application: note the Client ID, then Generate a new client secret and copy it once (GitHub shows it only now). Do not paste the secret into chat, tickets or the .env file; it goes into a file (below).
The callback URL must equal IDENTITY_PUBLIC_URL (scheme, host, port) plus the path above; Identity logs the exact URL it uses at startup (GitHub login is on, callback_url=…). GitHub never connects to your server: the user's browser carries the redirect, so a LAN-only installation works. Identity itself must reach github.com and api.github.com over HTTPS (outbound).
Requirements: IDENTITY_PUBLIC_URL is set to the address users open (HTTPS; http://localhost only for development), see Passkeys.
-
Put the client secret into a file the identity container (uid 10001) can read, without echoing it to the shell history:
cd infra/compose # e.g. /opt/mowdeck/infra/compose read -rs GH_SECRET && printf '%s' "$GH_SECRET" | sudo install -o 10001 -g 10001 -m 600 /dev/stdin secrets/github/client_secret && unset GH_SECRET
(
read -rswaits for the pasted secret and Enter.)infra/compose/secrets/github/is git-ignored and mounted read-only at/run/secrets/githubin the identity container. -
In
infra/compose/.env:IDENTITY_GITHUB_CLIENT_ID=<the Client ID> IDENTITY_GITHUB_CLIENT_SECRET_FILE=/run/secrets/github/client_secret
-
Restart Identity:
docker compose up -d identity(with a release file: the same-f docker-compose.yml -f docker-compose.release.ymlas for updates). Then check the log:docker compose logs identity | grep -i githubshowsGitHub login is onwith the callback URL, orGitHub login is offwith the reason.
kubectl -n mowdeck create secret generic mowdeck-github --from-file=client-secret=./client-secretservices:
identity:
github:
enabled: true
clientId: Iv23li... # the Client ID
existingSecret: mowdeck-github
secretKey: client-secret # the key in that Secret (default)
config:
IDENTITY_PUBLIC_URL: "" # empty: https://<ingress.host>The Secret is mounted as a file at /run/secrets/github/; the chart refuses to render with enabled: true but no clientId or existingSecret.
| Variable | Compose (.env) |
Default | Meaning |
|---|---|---|---|
MOWDECK_IDENTITY_GITHUB_CLIENT_ID |
IDENTITY_GITHUB_CLIENT_ID |
empty (off) | Client ID of the OAuth App |
MOWDECK_IDENTITY_GITHUB_CLIENT_SECRET_FILE |
IDENTITY_GITHUB_CLIENT_SECRET_FILE |
empty | File with the client secret |
MOWDECK_IDENTITY_GITHUB_AUTHORIZE_URL |
not set | https://github.com/login/oauth/authorize |
Only for GitHub Enterprise Server (https://<host>/login/oauth/authorize) |
MOWDECK_IDENTITY_GITHUB_TOKEN_URL |
not set | https://github.com/login/oauth/access_token |
Only for GitHub Enterprise Server |
MOWDECK_IDENTITY_GITHUB_API_URL |
not set | https://api.github.com |
GitHub Enterprise Server: https://<host>/api/v3
|
GitHub is on only when the client id, the secret and a usable IDENTITY_PUBLIC_URL are all set. A missing piece turns it off with an error in the log; the service and the password login keep running.
- Sign in with the password, Einstellungen, Konto, panel GitHub, Mit GitHub verknüpfen, enter the password, approve on GitHub. The panel then shows "Verknüpft mit GitHub als ".
- From then on Mit GitHub anmelden on the sign-in page (in the apps it opens the system browser like the passkey sign-in).
- Verknüpfung lösen removes the link at any time; the password keeps working.
See the user guide Signing in with GitHub.
- Scope
read:useronly; MowDeck reads GitHub's numeric user id (the link) and the login name (display). E-mail addresses are never requested. - Authorization code flow with PKCE S256; the single-use
stateis bound to the browser by anHttpOnlycookie holding the PKCE verifier (only its hash is stored); round trips expire after 10 minutes. - The GitHub access token is used for one request (
GET /user) and dropped; MowDeck never stores or logs it. It is not revoked: revoking an OAuth App's last token deletes the user's authorization, and GitHub would then show its consent page and e-mail "a third-party OAuth application was authorized" at every sign-in (ADR 0051, amendment 2026-10-25). GitHub revokes OAuth tokens unused for a year. - Unlinking in MowDeck does not remove the authorization on GitHub's side (MowDeck holds no token to do it with); the settings panel points the user to https://github.com/settings/applications.
- A GitHub account belongs to at most one MowDeck user and a user has at most one GitHub account. Linking needs the current password (the login limits apply).
- Rate limits: the callback and the link start are under the login limit (Traefik
identity-auth, Helm-authIngress, and in-process);POST /api/v1/identity/oauth/github/starthas the looser limit of the passkey challenge. - Audit log:
external_identity.linked,external_identity.unlinked,external_identity.link_failed,external_identity.reauth_failed,session.login/session.login_failedwithmethod: github. - Rotating the client secret: generate a new one on GitHub, replace the file, restart Identity, then delete the old secret on GitHub. Turning GitHub off (empty client id) keeps the links; users can still remove them.
Migration 0004 of Identity adds external_identities and oauth_states; it runs with the usual identity-migrate job. Downgrading to 0003 deletes all GitHub links (passwords and passkeys stay).
| Symptom | Cause |
|---|---|
| No "Mit GitHub anmelden" button, no GitHub panel | GitHub is off: see GitHub login is off in the Identity log (client id, secret file or IDENTITY_PUBLIC_URL missing or unusable) |
| GitHub shows "The redirect_uri is not associated with this application" | The callback URL of the OAuth App differs from the one Identity logs (host, port, scheme) |
| "GitHub ist gerade nicht erreichbar" | Identity cannot reach github.com / api.github.com, or the client secret is wrong (incorrect_client_credentials in the log) |
| "Anmeldung mit GitHub nicht möglich" | The GitHub account is not linked, or the user is disabled (session.login_failed in the audit log has the reason) |
| "Die Anmeldung mit GitHub ist abgelaufen" | More than 10 minutes on GitHub, cookies blocked, or another tab started a GitHub sign-in meanwhile |
| "Dieses GitHub-Konto ist schon mit einem anderen MowDeck-Benutzer verknüpft" | That user must unlink it first |
Mirrored from docs/ops/github-login.md at commit 73d5a3a260acf9fe900f15741d7bc8550f44be16. Edit the file in the repository, not this wiki page.
Mirrored from the docs/ directory at commit 73d5a3a260acf9fe900f15741d7bc8550f44be16. Edit the files in the repository, not this wiki.
MowDeck documentation
- Home
- Phases
- Architecture
-
Decisions (ADR)
- Architecture decisions
- ADR 0001: OpenFGA instead of self-built authorization
- ADR 0002: Self-built Identity service in FastAPI instead of Keycloak or Zitadel
- ADR 0003: SvelteKit for the web app
- ADR 0004: Monorepo with a uv workspace
- ADR 0005: AGPL-3.0 for services and apps, Apache-2.0 for libraries
- ADR 0006: PostgreSQL for all persistence, no SQLite
- ADR 0007: NATS JetStream as event bus
- ADR 0008: Traefik as gateway
- ADR 0009: Command and event envelopes, subject naming and de-duplication
- ADR 0010: Python 3.13 for all services and libraries
- ADR 0011: Separate GPL-3.0 package for the PyMammotion adapter
- ADR 0012: Mower control interface and safety rules
- ADR 0013: Device-Gateway registry, sharding and per-mower concurrency
- ADR 0014: Authorization model, tuple ownership and the mowdeck-authz client
- ADR 0015: Tokens, sessions and key rotation
- ADR 0016: Fleet commands and authorization per command kind
- ADR 0017: Telemetry storage and retention
- ADR 0018: Realtime WebSocket protocol
- ADR 0019: Mammotion adapter transports, manual drive and secrets
- ADR 0020: Web app architecture
- ADR 0021: Browser authentication flow
- ADR 0022: Notifications: rules, recipients and delivery
- ADR 0023: Map rendering and projection
- ADR 0024: Scheduler: recurrence, rain pause and command issuing
- ADR 0025: Home Assistant integration as a pure API client
- ADR 0026: Manual drive channel over the Realtime WebSocket
- ADR 0027: Health history and theft alarm
- ADR 0028: Entitlements: plans as data and enforcement
- ADR 0029: Helm chart and release pipeline
- ADR 0030: Mowing patterns as plug-ins and the Planner
- ADR 0031: Billing with a Stripe adapter
- ADR 0032: Live camera over Agora, joined directly by the browser
- ADR 0033: Vision-assisted recovery with human approval
- ADR 0034: Linked cloud accounts and device discovery
- ADR 0035: Server-side camera frames and automatic AI analysis on errors
- ADR 0036: Native app shells with Capacitor, loading the web app from the server
- ADR 0037: Camera panorama calibration in the browser, with the AI supplying points only
- ADR 0038: Dual link per mower and firmware updates
- ADR 0039: Spot mowing by MowDeck-driven paths
- ADR 0040: Link heartbeat for online status
- ADR 0041: Spot runs from the dock: undock, transit and return
- ADR 0042: Drive calibration
- ADR 0043: Turn styles
- ADR 0044: Drive view with camera
- ADR 0045: Camera relay
- ADR 0046: Device settings as capabilities
- ADR 0047: The RTK base station as a Fleet device
- ADR 0048: Maintenance commands
- ADR 0049: Manual driving over the Mammotion cloud
- ADR 0050: Passkeys and the app login hand-off
- ADR 0051: Sign in with GitHub
- ADR 0052: Sign in with Apple
- Development
- Operations
- Spikes
- Guides
- Original plan (German)