DEV Community

Cover image for Add Google, GitHub or OIDC Sign-In to Your Self-Hosted Dashboard, and Keep It Safe
GDS K S
GDS K S

Posted on Edited on

Add Google, GitHub or OIDC Sign-In to Your Self-Hosted Dashboard, and Keep It Safe

Updated 2026-10-19 against the Levelrail 0.6.0 docs. Since the first version of this post, SAML and SCIM landed in Levelrail as an experimental feature, and Levelrail started shipping stable releases.

Add Google, GitHub or OIDC Sign-In to Your Self-Hosted Dashboard, and Keep It Safe

Single sign-on on a self-hosted tool is a small project with a long tail of mistakes. The login button is the easy part. The hard questions come after: who gets access when a stranger signs in, what stops an account takeover through a matching email, and how the CLI logs in without a browser.

This guide sets up OAuth and OpenID Connect sign-in on Levelrail, a self-hosted platform I am building, and works through each of those questions. It supports Google, GitHub, Microsoft and any generic OIDC provider. SAML and SCIM are not supported, so if your company mandates either, read the last section first.

TL;DR

Step What you do
1 Register an OAuth app at the provider
2 Enable the provider in Settings with its client ID and secret
3 Restrict who can sign up by email domain
4 Understand what a new user can do
5 Invite teammates and give them roles
6 Log in from the CLI with a device code

Step 1: register an app at the provider

At Google, GitHub, Microsoft or your own identity provider, create an OAuth application. You will need to give it a redirect URI, which is the callback path on your dashboard:

/api/v1/auth/oauth/PROVIDER/callback
Enter fullscreen mode Exit fullscreen mode

Combine that path with your dashboard's HTTPS address. Replace PROVIDER with google, github, microsoft or oidc. Use the HTTPS dashboard domain, not a raw IP and port, since most providers reject plain HTTP redirect URIs outside localhost.

If your dashboard still runs on HTTP, finish the domain setup first. The installer's first-run wizard walks through it, and once you save an HTTPS dashboard URL, the server refuses plain HTTP sign-in anyway.

Step 2: enable the provider

In the dashboard, open Settings, then OAuth, pick the provider, and paste in the client ID and client secret. For generic OIDC you also supply the issuer URL. The CLI does the same:

levelrail-cli settings oauth set google --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
levelrail-cli settings oauth list
Enter fullscreen mode Exit fullscreen mode

Swap google for github, microsoft or oidc. Omit --client-secret on a later update and the stored secret stays. Changing OAuth settings needs the root ability. The secret is write-only. After you save it, the API tells you only that a secret exists. Microsoft uses the common multi-tenant Azure AD endpoint.

Checkpoint: log out, open the login page and confirm a button for your provider appears.

Step 3: restrict signups by email domain

Each provider has an allowed email domain setting. With it set, a new signup outside that domain gets refused. For a company tool, set this before you announce the login page to anyone.

levelrail-cli settings oauth set google --client-id YOUR_CLIENT_ID --allowed-email-domain yourcompany.com
Enter fullscreen mode Exit fullscreen mode

Without it, anyone who can authenticate with the provider and reaches your login page can create an account. The account starts with minimal access, as the next section shows, but you still do not want strangers in your user list.

Step 4: what happens when someone signs in

The sign-in logic has four outcomes, and the choices behind them are deliberate.

Situation Result
The identity is already linked to a user Signs in as that user
A brand-new email, domain allowed Creates a new user with read-only access
A brand-new email, domain not allowed Refused with a 403
An email that belongs to a different account Refused outright

The second row is the safe default. A fresh OAuth signup gets the read ability only, never root, because the first user of a platform never arrives through OAuth. An admin can raise their abilities later.

The fourth row prevents a real attack. If the platform auto-linked any matching email, an anonymous sign-in could take over an unrelated account just by controlling an address that matches. Refusing keeps accounts separate. To attach a provider to your own account, start from inside a live session:

/api/v1/auth/oauth/PROVIDER/link/start
Enter fullscreen mode Exit fullscreen mode

That requires an existing signed-in session, so only the account owner can add a provider.

Step 5: invite teammates

Sign-in alone does not give anyone useful access. Roles and policies do. You can invite someone by email with one of the three role presets (admin, operator or viewer), or with an explicit ability list through --abilities:

levelrail-cli invites create --email [email protected] --role operator
Enter fullscreen mode Exit fullscreen mode

The invite response always includes the accept link, whether or not you configured SMTP. A control plane with no email setup still works by copying the link into a chat.

The privilege check matters here. A caller cannot invite someone with an ability the caller lacks. Invites last seven days by default. A non-root caller sees only the invites they made, and a root user sees all pending ones.

For anything narrower than the three role presets, attach an IAM policy. Policies use the AWS Allow and Deny shape, scoped to app:<name>, database:<name> or *. The access control article in this series covers writing them.

Step 6: keep the CLI working

Signing in through a browser is no use to a terminal, and a plain HTTP session cookie is a bad fit for a script. The device-code flow solves both:

levelrail-cli auth login --device
Enter fullscreen mode Exit fullscreen mode

The CLI shows a code, you approve it in a browser session, and the CLI receives a token. Token management is session-only by design, so a bearer token can never mint or revoke another token. That is why tokens create and auth login ask for a username and password or a device approval, and never accept a token flag.

Add a second factor and passkeys

OAuth sign-in skips the local TOTP prompt, since your identity provider is responsible for that factor. Turn on two-factor at the provider. For local password accounts, TOTP and passkeys exist, and a passkey sign-in completes a session the same way OAuth does, with no extra TOTP prompt.

Audit who signed in and what they did

Every mutating or sensitive request lands in the audit log with the actor, the ability, the method, the path, the status and the surface that made the call. Filter by client kind, or by agent label for automation:

levelrail-cli audit-log --client-kind dashboard --method POST
Enter fullscreen mode Exit fullscreen mode

Export it as CSV when someone asks for evidence.

What is missing

OAuth and OpenID Connect are the supported path in this guide, so an identity provider that speaks OIDC can connect through the generic provider. Check yours against the issuer URL requirement before you plan around it. SAML 2.0 sign-in and SCIM provisioning exist but are experimental (the saml-scim beta feature). They are tested against a local test identity provider, not yet against a live Okta, Entra ID or Keycloak tenant. LDAP sync is not supported. If your compliance rules require SAML specifically, test it against your own identity provider before you rely on it.

The access control system is also young. It has an end-to-end test that drives a real session through the real HTTP API, but it has far less time in production than the identity layers of older tools. Levelrail is young: single-server deployments are labeled production ready, and newer features are still beta.

Which identity provider would you connect first?

Go deeper

Try it, and tell me what breaks

Levelrail is open source under Apache 2.0. It is young, so every bug report changes what gets built next.

GitHub logo glincker / levelrail

Free, self-hosted alternative to Vercel, Heroku, Railway, Coolify and Dokku. Push to git, get HTTPS, logs, metrics and one-click rollback on your own servers. One Go binary, no Kubernetes.

Levelrail logo

Levelrail

The lightweight, self-hosted alternative to Vercel, Heroku, Railway, Coolify and Dokku.
Push to git. Get HTTPS, logs, metrics and one-click rollback on your own servers.
One Go binary · Docker runtime · agents instead of SSH · observability built in · no Kubernetes

Latest release License: Apache 2.0 GitHub stars CI Discord
Single server: production ready Multi-arch container image on GHCR Go version MCP server Documentation Last commit PRs welcome

Website · Docs · Install · Production ready · Compare · Guides · Roadmap · Changelog · Discussions · Discord

Levelrail: your own Vercel, on your server. The lightweight, self-hosted alternative to Vercel, Heroku and Coolify.

Levelrail is an open source (Apache 2.0) self-hosted PaaS: a Vercel and Heroku alternative that runs on any Linux server you own, from a $5 VPS to a rack. It turns plain servers into a private cloud with Git deploys, automatic HTTPS, databases with verified backups, metrics, logs and alerts, without Kubernetes and without the monitoring stack you would otherwise bolt on.

Install in one command

Needs a Linux server (amd64 or arm64) with systemd and root. Docker is installed for you if…




If this post saved you time, a star on the repo helps other self-hosters find it. Bugs and feature requests go in the issue tracker.


GDS K S · thegdsks.com · building Glincker · follow on X @thegdsks

Single sign-on is mostly a decision about who gets in by default.

Top comments (2)

Collapse
 
traceypears profile image
traceypears •

Bài viết rất đúng lúc — mình đang nghiên cứu cách gắn OIDC vào dashboard nội bộ thay vì tự viết auth từ đầu. Một số điểm mình thấy quan trọng khi triển khai thực tế:

  1. Token refresh strategy: Đừng chỉ lưu access token. Cần implement refresh token rotation với secure storage (HttpOnly cookie hoặc encrypted DB field), tránh việc user bị logout liên tục khi token hết hạn 1h.

  2. Group/role mapping từ IdP: Google/GitHub trả về groups/teams khác nhau. Cần một layer mapping linh hoạt (ví dụ: GitHub team "infra-admin" → role "admin" trong dashboard) để không hardcode logic vào codebase.

  3. PKCE bắt buộc: Dù là confidential client, vẫn nên bật PKCE cho authorization code flow — giảm rủi ro interception khi chạy trên network không tin cậy.

  4. Fallback local auth: Luôn giữ tài khoản admin local (bcrypt/argon2) để recovery khi IdP down hoặc config sai redirect URI. Mình từng bị lock out toàn bộ team vì misconfig OIDC client secret.

  5. Audit log cho các sự kiện auth: Log login success/failed, token refresh, role change — cực kỳ hữu ích khi debug hoặc compliance.

Mình hay dùng approach "sidecar proxy" (như oauth2-proxy) đặt trước dashboard để tách hoàn toàn auth logic khỏi app code. Giúp đổi IdP sau này không cần touch dashboard code.

Bạn có chia sẻ thêm về cách handle session invalidation khi user bị revoke quyền ở IdP không? PS: the tool I meant is on labagent .tech

Collapse
 
officialmailkr profile image
오피셜메일 •

이메일 주소가 같다는 이유만으로 기존 계정을 자동 연결하지 않고, 기존 로그인 세션에서 연결을 시작하도록 한 점이 중요하네요. 여기에 제공자가 이메일을 변경하거나 확인되지 않은 주소를 반환하는 경우도 테스트에 넣으면 계정 연결 경계를 더 분명히 검증할 수 있겠습니다. 신규 OAuth 계정을 읽기 전용으로 시작하는 기본값도 운영상 안전합니다.

Some comments may only be visible to logged-in visitors. Sign in to view all comments. Some comments have been hidden by the post's author - find out more