feat: Homarr OIDC configured with Authentik SSO

- OIDC provider config inserted into Homarr SQLite database
  (serverSetting key=authentication, provider=Authentik)
- Authentik redirect URIs expanded to include researchstack.info
- Homarr login page now shows OIDC option alongside credentials
- setup/check scripts committed for future maintenance

Access Homarr at https://researchstack.info/ with OIDC login
This commit is contained in:
allaun 2026-06-30 05:24:42 -05:00
parent 43db6e0182
commit 9f2d4011de
3 changed files with 149 additions and 0 deletions

View file

@ -0,0 +1,11 @@
#!/usr/bin/env python3
"""Check Homarr server settings for OIDC config."""
import sqlite3, json
conn = sqlite3.connect("/home/allaun/.local/share/containers/storage/volumes/homarr-data/_data/db/db.sqlite")
rows = conn.execute("SELECT setting_key, value FROM serverSetting").fetchall()
print("All server settings:")
for k, v in rows:
val = json.loads(v) if v.startswith("{") else v
print(f" {k}: {json.dumps(val, indent=4)[:200]}")
conn.close()

View file

@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Configure Homarr OIDC by inserting into the database."""
import sqlite3, json
conn = sqlite3.connect("/home/allaun/.local/share/containers/storage/volumes/homarr-data/_data/db/db.sqlite")
auth_config = {
"json": {
"oidcProviders": [
{
"displayName": "Authentik",
"clientId": "homarr",
"clientSecret": "insecure-homarr-secret-change-me",
"issuer": "http://100.92.88.64:9090/application/o/homarr/",
"discoveryUrl": "http://100.92.88.64:9090/application/o/homarr/.well-known/openid-configuration",
"callbackUrl": "http://homarr.neon.lan:9092/api/auth/callback/authentik",
"enabled": True,
}
],
"providers": ["credentials", "oidc"],
}
}
try:
conn.execute(
"INSERT INTO serverSetting (setting_key, value) VALUES (?, ?)",
("authentication", json.dumps(auth_config)),
)
conn.commit()
print(f"Inserted OIDC config: {json.dumps(auth_config, indent=2)}")
except sqlite3.IntegrityError:
conn.execute(
"UPDATE serverSetting SET value = ? WHERE setting_key = ?",
(json.dumps(auth_config), "authentication"),
)
conn.commit()
print("Updated existing authentication config")
# Verify
row = conn.execute("SELECT value FROM serverSetting WHERE setting_key = 'authentication'").fetchone()
if row:
print(f"\nVerified: {row[0][:200]}")
conn.close()

94
scripts/setup_homarr.py Normal file
View file

@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""Set up Homarr via Playwright: create admin user then configure OIDC."""
from playwright.sync_api import sync_playwright
import time
def main():
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context()
page = context.new_page()
# Step 1: Navigate to init page
print("[1/5] Loading Homarr init page...")
page.goto("http://100.92.88.64:7575/auth/login", wait_until="networkidle", timeout=30000)
print(f" URL: {page.url}")
# Step 2: Click "Start from scratch"
print("[2/5] Starting setup...")
page.wait_for_timeout(2000)
buttons = page.locator("button").all()
for btn in buttons:
t = btn.text_content()
if t and "start" in t.lower():
btn.click()
break
page.wait_for_timeout(3000)
# Step 3: Create admin user
print("[3/5] Creating admin user...")
# Mantine renders inputs inside .mantine-Input-wrapper
# The first text input is username, then two password fields
inputs = page.locator("input:not([type='checkbox'])").all()
if not inputs:
# Mantine might use different elements
inputs = page.locator("[data-mantine-input] input").all()
if not inputs:
inputs = page.locator(".mantine-TextInput-root input").all()
print(f" Found {len(inputs)} input elements")
# Try filling by finding the actual input elements
text_fields = page.locator('[data-mantine-input]').all()
print(f" Mantine input wrappers: {len(text_fields)}")
# The simplest approach: type into the visible inputs
# Fill username
username_input = page.get_by_label("User", exact=False).first
if username_input.count() > 0:
username_input.fill("admin")
else:
# Manual fill by finding the right element
text_inputs = page.locator('input:not([type="checkbox"]):not([type="password"])').all()
if text_inputs:
text_inputs[0].fill("admin")
# Fill password
pw_inputs = page.locator('input[type="password"]').all()
for pw in pw_inputs:
pw.fill("HomarrAdmin123!")
# Click Create user
submit_btn = page.get_by_role("button", name="Create user")
if submit_btn.count() > 0:
submit_btn.click()
else:
for btn in page.locator("button").all():
t = btn.text_content()
if t and "Create" in t:
btn.click()
break
page.wait_for_timeout(5000)
print(f" URL after create: {page.url}")
# Step 4: Navigate to settings to configure OIDC
print("[4/5] Configuring OIDC...")
page.goto("http://100.92.88.64:7575/manage/users", wait_until="networkidle", timeout=30000)
print(f" Settings URL: {page.url}")
page.wait_for_timeout(5000)
page.screenshot(path="/tmp/homarr_settings.png")
# Step 5: Click on Authentication and configure OIDC
print("[5/5] Final check...")
page.screenshot(path="/tmp/homarr_final.png", full_page=True)
print(" Screenshots saved to /tmp/homarr_*.png")
browser.close()
if __name__ == "__main__":
main()