Skip to content

feat: oidc login support#172

Merged
kmendell merged 14 commits intomainfrom
feat/oidc-login
May 13, 2025
Merged

feat: oidc login support#172
kmendell merged 14 commits intomainfrom
feat/oidc-login

Conversation

@kmendell
Copy link
Member

@kmendell kmendell commented May 12, 2025

fixes: #110

Summary by CodeRabbit

  • New Features

    • Added support for OpenID Connect (OIDC) authentication, including configuration via environment variables or application settings.
    • Introduced OIDC login flow with new endpoints for login and callback.
    • Added UI for configuring OIDC in authentication settings, with environment-aware behavior and validation.
    • Users can now be sourced from OIDC providers, with clear indication in the user management table.
  • Enhancements

    • Improved login page to conditionally display local and OIDC login options based on configuration.
    • Enhanced error handling and feedback for OIDC authentication scenarios.
    • Settings loading and saving now robustly merge defaults, support OIDC injection, and handle encryption consistently.
  • Bug Fixes

    • Prevented potential errors in password verification by adding type checks.
  • Chores

    • Updated example environment file and added required dependencies.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 12, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This change introduces comprehensive OpenID Connect (OIDC) authentication support. It adds OIDC configuration options to environment variables, settings types, and the authentication settings UI. New routes and services implement the OIDC login and callback flow, user linking/creation by OIDC subject, and conditional login logic. User types and management UI are updated to support OIDC users.

Changes

Files/Groups Change Summary
.env.example Added OIDC-related environment variables as configuration examples.
package.json Added the "arctic" package as a new dependency.
src/hooks.server.ts Allowed OIDC login and callback routes to bypass authentication checks.
src/lib/services/oidc-service.ts New service: Resolves OIDC configuration from environment or settings, validates required fields, and exports OAuth2 client and OIDC config.
src/lib/services/settings-service.ts Refactored settings loading/merging, added OIDC config injection from environment, updated encryption/decryption logic, and added oidcEnabled flag to defaults.
src/lib/services/user-service.ts Added getUserByOidcSubjectId function; made verifyPassword robust to missing password hash.
src/lib/stores/settings-store.ts Added oidcEnabled boolean to settings store's auth object.
src/lib/types/settings.type.ts Extended AuthSettings with oidcEnabled and optional oidc config; added OidcConfig interface.
src/lib/types/user.type.ts Made passwordHash optional; added optional oidcSubjectId to User type.
src/routes/auth/login/+page.server.ts, src/routes/auth/login/+page.svelte Refactored login logic/UI: conditional rendering for local/OIDC login, error handling, and redirect logic; disables local login for OIDC-only users.
src/routes/auth/oidc/login/+server.ts, src/routes/auth/oidc/callback/+server.ts Added OIDC login initiation and callback handlers: manage PKCE, state, token exchange, user info retrieval, user linking/creation, and session creation.
src/routes/settings/+page.server.ts Added check for OIDC environment variable configuration and exposed result to settings page.
src/routes/settings/tabs/authentication.svelte Added OIDC authentication configuration UI: enable/disable switch, dialog for config, environment-aware behavior, and persistence. Updated local login switch logic.
src/routes/settings/tabs/user-management.svelte Added "Source" column to user table to indicate OIDC or Local users; disabled editing for OIDC users.

Assessment against linked issues

Objective Addressed Explanation
Add OIDC/OAuth2 authentication option (#110)
Support OIDC configuration via environment variables and application settings (#110)
Implement OIDC login and callback flow, including user linking/creation by OIDC subject (#110)
Update user types, login logic, and management UI for OIDC users (#110)
Add OIDC configuration and enable/disable controls to authentication settings UI (#110)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@kmendell kmendell marked this pull request as ready for review May 12, 2025 22:11
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🔭 Outside diff range comments (1)
src/routes/settings/tabs/user-management.svelte (1)

73-74: ⚠️ Potential issue

Title mismatch in confirmation dialog

The confirmation dialog for deleting a user is titled "Delete Container" and refers to "container" in the message, which is inconsistent with the actual action of deleting a user.

-			title: 'Delete Container',
-			message: 'Are you sure you want to delete this container? This action cannot be undone.',
+			title: 'Delete User',
+			message: 'Are you sure you want to delete this user? This action cannot be undone.',
🧹 Nitpick comments (12)
src/routes/settings/tabs/user-management.svelte (1)

106-106: Update description to include OIDC users

The card description still refers only to "local user accounts" despite now displaying both local and OIDC users.

-					<Card.Description>Manage local user accounts</Card.Description>
+					<Card.Description>Manage user accounts (local and OIDC)</Card.Description>
.env.example (2)

4-12: Comprehensive OIDC configuration variables

The OIDC environment variables provide a complete set of configuration options needed for OIDC integration, including client credentials, endpoints, and scopes.

However, consider adding documentation for the required format of the OIDC provider URLs and any provider-specific requirements.

 # OIDC Provider Settings
 PUBLIC_OIDC_ENABLED=false
 OIDC_CLIENT_ID="your_oidc_client_id"
 OIDC_CLIENT_SECRET="your_oidc_client_secret"
 OIDC_REDIRECT_URI="http://localhost:5173/auth/oidc/callback"
+# Full URLs including https:// prefix
 OIDC_AUTHORIZATION_ENDPOINT="your_oidc_provider_authorization_endpoint_url"
 OIDC_TOKEN_ENDPOINT="your_oidc_provider_token_endpoint_url"
 OIDC_USERINFO_ENDPOINT="your_oidc_provider_userinfo_endpoint_url"
 OIDC_SCOPES="openid email profile" # Space separated list of scopes

6-8: Default OIDC client settings

The default settings provide a good template for configuration, but developers might need guidance on where to obtain these values.

-OIDC_CLIENT_ID="your_oidc_client_id"
-OIDC_CLIENT_SECRET="your_oidc_client_secret"
+# Register an application with your OIDC provider to obtain these values
+OIDC_CLIENT_ID="your_oidc_client_id"
+OIDC_CLIENT_SECRET="your_oidc_client_secret"
src/lib/types/settings.type.ts (1)

3-8: Consider default handling and documentation for new OIDC flags

oidcEnabled and the optional oidc object are now first-class citizens in AuthSettings, but code that consumes this interface may still assume they are absent.

• Provide a sensible default (false / undefined) wherever the settings object is instantiated or parsed, otherwise a missing key will coerce to undefined and may trip strict boolean checks (=== true).
• Add concise JSDoc describing the precedence rules between env-forced and settings-driven flags to help future maintainers.

src/lib/services/user-service.ts (1)

124-128: Possible performance & integrity improvements for OIDC subject lookup

getUserByOidcSubjectId() loads all user files every call and stops at the first match.

• If the user base grows, this becomes O(N). Caching a Map<oidcSubjectId, id> in memory (refreshed on writes) would make look-ups O(1).
• If two users accidentally share the same oidcSubjectId, the function silently returns the first; consider detecting duplicates and logging an error.

src/routes/auth/login/+page.svelte (2)

21-27: Boolean env parsing may mis-behave

env.PUBLIC_OIDC_ENABLED === 'true' is strict; any uppercase variant ('TRUE') or inadvertent whitespace will disable OIDC. A helper such as:

const toBool = (v?: string) => /^true$/i.test(v?.trim() ?? '');
const oidcForcedByEnv = toBool(env.PUBLIC_OIDC_ENABLED);

makes the check more robust.


44-68: Collapse verbose if/else chain into a lookup table

The long conditional block for error messages is hard to maintain. A keyed map keeps the markup concise and makes adding new codes trivial.

<script lang="ts">
  const errorMessages: Record<string, string> = {
    oidc_invalid_response: 'There was an issue with the OIDC login response. Please try again.',
    oidc_misconfigured:   'OIDC is not configured correctly on the server. Please contact an administrator.',
    oidc_userinfo_failed: 'Could not retrieve your user information from the OIDC provider.',
    oidc_missing_sub:     'Your OIDC provider did not return a subject identifier.',
    oidc_email_collision: 'An account with your email already exists but is linked to a different OIDC identity. Please contact an administrator.',
    oidc_token_error:     'There was an error obtaining tokens from the OIDC provider.',
    user_processing_failed:'An error occurred while processing your user account.'
  };
</script>

{#if data.error}
  <Alert.Root class="mb-4" variant="destructive">
    <AlertCircle class="h-4 w-4 mr-2" />
    <Alert.Title>Login Problem</Alert.Title>
    <Alert.Description>{errorMessages[data.error] ?? 'An unexpected error occurred. Please try again.'}</Alert.Description>
  </Alert.Root>
{/if}
src/routes/auth/login/+page.server.ts (1)

48-53: Duplicate settings fetch inside hot path

getSettings() is executed once in load (line 9) and again in the login action (line 48).
If the call is I/O-bound (e.g. decrypting a file on disk) this doubles the latency for every login attempt.

Either:

  • Pass appSettings to the action via locals, or
  • Cache the result in a singleton/settings store that survives for the lifetime of the server process.

This keeps the handler fast under load.

src/lib/services/oidc-service.ts (1)

5-5: Top-level await blocks the whole module graph

const settings = await getSettings(); executes during module initialisation.
If getSettings() performs file I/O or crypto it will stall every import that transitively depends on this file, increasing cold-start time and delaying the first request.

Consider:

  • Calling getSettings() lazily inside a getResolvedConfig() helper, or
  • Performing the fetch once and caching the promise in a lightweight, eagerly-resolved module.

This keeps other parts of the app from being held hostage by configuration I/O.

src/routes/auth/oidc/callback/+server.ts (1)

4-4: Unused import – tidy up

getUserById is imported but never referenced, adding dead code and increasing bundle size (albeit minimally).

-import { getUserByUsername, saveUser, getUserById, getUserByOidcSubjectId } from '$lib/services/user-service';
+import { getUserByUsername, saveUser, getUserByOidcSubjectId } from '$lib/services/user-service';
src/lib/services/settings-service.ts (1)

108-139: Consider extracting the settings-merge logic into a reusable helper

The two blocks that build effectiveSettings for encrypted vs. plain files are almost identical, differing only in the source object (decryptedData vs. settingsFromFile). Duplicating this deep-merge code:

  1. Makes future changes error-prone (you must patch both branches).
  2. Hides intent—readers have to diff the two fragments mentally.
  3. Slightly inflates bundle size.

A tiny utility such as mergeSettings(base, sensitive?, nonSensitive?) (or leveraging a library deepmerge) would remove ~25 lines here and improve testability.

src/routes/settings/tabs/authentication.svelte (1)

140-175: Nested if / {#if} blocks are getting hard to scan

The status banner logic now has five nested conditions & four mutually exclusive branches. Consider:

  1. Extracting a small helper function that returns { variant, message }.
  2. Or moving this block into its own <OidcStatusBanner> component.

This keeps the main file readable and makes unit-testing the individual paths easier.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50cb424 and b58aea1.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (16)
  • .env.example (1 hunks)
  • package.json (1 hunks)
  • src/hooks.server.ts (1 hunks)
  • src/lib/services/oidc-service.ts (1 hunks)
  • src/lib/services/settings-service.ts (4 hunks)
  • src/lib/services/user-service.ts (2 hunks)
  • src/lib/stores/settings-store.ts (1 hunks)
  • src/lib/types/settings.type.ts (2 hunks)
  • src/lib/types/user.type.ts (1 hunks)
  • src/routes/auth/login/+page.server.ts (3 hunks)
  • src/routes/auth/login/+page.svelte (4 hunks)
  • src/routes/auth/oidc/callback/+server.ts (1 hunks)
  • src/routes/auth/oidc/login/+server.ts (1 hunks)
  • src/routes/settings/+page.server.ts (2 hunks)
  • src/routes/settings/tabs/authentication.svelte (3 hunks)
  • src/routes/settings/tabs/user-management.svelte (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/lib/services/user-service.ts (1)
src/lib/types/user.type.ts (1)
  • User (1-13)
src/routes/auth/oidc/callback/+server.ts (5)
src/routes/auth/oidc/login/+server.ts (1)
  • GET (6-43)
src/lib/services/oidc-service.ts (3)
  • OIDC_TOKEN_ENDPOINT (42-42)
  • oidcClient (54-54)
  • OIDC_USERINFO_ENDPOINT (43-43)
src/lib/types/user.type.ts (1)
  • User (1-13)
src/lib/services/user-service.ts (3)
  • getUserByOidcSubjectId (124-128)
  • getUserByUsername (14-36)
  • saveUser (55-75)
src/lib/types/session.type.ts (1)
  • UserSession (1-7)
🔇 Additional comments (13)
src/lib/stores/settings-store.ts (1)

38-38:

Details

✅ Verification successful

Support for OIDC authentication added

The oidcEnabled flag has been appropriately added to the auth settings object, initialized to false by default. This change aligns with the PR objective of adding OIDC login support.


🏁 Script executed:

#!/bin/bash
# Verify that the new oidcEnabled flag is properly handled in related components

# Check if the authentication settings UI uses this flag
echo "Checking for oidcEnabled usage in settings UI..."
rg "oidcEnabled" "src/routes/settings/tabs/authentication.svelte" -A 5 -B 5

# Check if there's proper OIDC service initialization based on this flag
echo "Checking for OIDC service initialization..."
rg "oidcEnabled" "src/lib/services" -A 5 -B 5

Length of output: 4688


OIDC Enabled Flag Properly Integrated

The oidcEnabled flag is now consistently handled across the codebase:

  • Default initialized in
    • src/lib/stores/settings-store.ts
    • src/lib/services/settings-service.ts
  • Consumed in the settings UI (src/routes/settings/tabs/authentication.svelte) for switch state, forced-on logic, and dialog display
  • Used in OIDC initialization (src/lib/services/oidc-service.ts) to apply app-level OIDC settings as fallbacks

All usages align with the intended behavior.

package.json (1)

60-60:

Details

✅ Verification successful

Arctic library added for OIDC implementation

The addition of the Arctic library (v3.6.1) is appropriate for implementing OIDC authentication functionality.


🌐 Web query:

Is arctic 3.6.1 the latest version and what are its security considerations?

💡 Result:

The term "Arctic 3.6.1" could refer to different software products, as "Arctic" is used by multiple projects. Here are some possibilities:

  1. Arctic Security's Arctic Hub: A cybersecurity platform.
  2. Arctic by Man AHL: A high-performance datastore for time series and tick data.
  3. Arctic JavaScript Library: An OAuth 2.0 client library.

Could you please specify which "Arctic" product you're referring to? This will help me provide accurate information regarding its latest version and security considerations.


To determine which “arctic” package this refers to, let’s inspect how it’s imported/used in the codebase:


🏁 Script executed:

#!/bin/bash
# Search for import or require statements referencing 'arctic'
rg -n "import .*arctic" .
rg -n "require('arctic')" .

Length of output: 354


Let’s fetch the npm registry metadata for the arctic package to confirm its exact name, description, and latest version:


🏁 Script executed:

#!/bin/bash
# Fetch package metadata for the 'arctic' npm package
curl -s https://registry.npmjs.org/arctic | jq '.name, .description, .["dist-tags"].latest'

Length of output: 152


Confirm Arctic v3.6.1 and security status

  • npm registry shows arctic@3.6.1 as the current latest release.
  • Description: “OAuth 2.0 clients for popular providers.”
  • No security advisories are reported in the npm registry for this version.

This dependency is appropriate for your OIDC implementation. Be sure to monitor for future vulnerability disclosures.

src/hooks.server.ts (1)

35-35:

Details

✅ Verification successful

OIDC authentication endpoints added to public paths

The OIDC login and callback endpoints have been properly added to the public paths array, allowing unauthenticated access to these endpoints as required for the OIDC authentication flow.


🏁 Script executed:

#!/bin/bash
# Verify the implementation of the OIDC endpoints to ensure they handle authentication correctly

# Check if OIDC login endpoint exists and redirects to the OIDC provider
echo "Checking OIDC login endpoint implementation..."
rg -A 10 "export.*GET|export.*POST" "src/routes/auth/oidc/login"

# Check if OIDC callback endpoint exists and properly processes the authentication response
echo "Checking OIDC callback endpoint implementation..."
rg -A 15 "export.*GET|export.*POST" "src/routes/auth/oidc/callback"

Length of output: 2597


Verified: OIDC endpoints correctly whitelisted and implemented

  • src/hooks.server.ts now includes /auth/oidc/login and /auth/oidc/callback in its publicPaths.
  • src/routes/auth/oidc/login/+server.ts exports a GET handler that builds the PKCE authorization URL and redirects, with proper error handling for a missing OIDC_AUTHORIZATION_ENDPOINT.
  • src/routes/auth/oidc/callback/+server.ts exports a GET handler that retrieves and validates code, state, and stored cookies, cleans up on both success and failure, and redirects appropriately on any mismatch.

No further changes required—approving these updates.

src/lib/types/user.type.ts (1)

4-4:

Details

✅ Verification successful

User type updated to support OIDC authentication

The User type has been updated to make passwordHash optional and add the oidcSubjectId field, which are necessary changes to support users authenticating via OIDC without local passwords.

Also applies to: 12-12


🏁 Script executed:

#!/bin/bash
# Verify that the user service properly handles OIDC users

# Check if the user service has been updated to handle the optional passwordHash
echo "Checking handling of optional passwordHash in user service..."
rg "passwordHash" "src/lib/services/user-service.ts" -A 5 -B 5

# Check if there's functionality to find users by OIDC subject ID
echo "Checking for OIDC subject ID lookup functionality..."
rg "oidcSubjectId" "src/lib/services/user-service.ts" -A 5 -B 5

# Check if the login flow handles users without passwords
echo "Checking login flow for OIDC users..."
rg -A 10 -B 10 "oidcSubjectId|passwordHash" "src/routes/auth"

Length of output: 8756


User type OIDC support verified and approved

All necessary updates for optional passwordHash and oidcSubjectId support have been implemented correctly:

  • verifyPassword in src/lib/services/user-service.ts returns false when passwordHash is absent.
  • getUserByOidcSubjectId is implemented in src/lib/services/user-service.ts.
  • Local login handler in src/routes/auth/login/+page.server.ts rejects OIDC-only users without a passwordHash.
  • OIDC callback in src/routes/auth/oidc/callback/+server.ts creates and updates users with oidcSubjectId as expected.

No further action required.

src/routes/settings/tabs/user-management.svelte (2)

123-123: Clean implementation of user source display

The addition of a "Source" column with status badges clearly distinguishes between OIDC and local users, which is essential for administrators to understand user origins.

Also applies to: 163-165


176-181: Appropriate restriction of OIDC user editing

The conditional rendering of the "Edit" option only for local users is a crucial security measure that prevents modification of externally managed OIDC user accounts.

src/routes/settings/+page.server.ts (2)

17-19: Well-structured OIDC environment variable check

The comprehensive check for all required OIDC environment variables ensures that the UI can accurately reflect the configuration status.


24-24: Clear export of OIDC configuration status

Exporting the OIDC environment configuration status to the client side is essential for proper UI rendering based on available configuration options.

src/routes/auth/oidc/login/+server.ts (3)

12-15: Secure implementation of OIDC authorization with PKCE

The implementation correctly uses PKCE (Proof Key for Code Exchange) with the S256 code challenge method, which enhances security by preventing authorization code interception attacks.


17-40: Secure cookie configuration for OIDC state

The cookies for OIDC state, code verifier, and redirect URL are properly configured with security best practices:

  • HTTP-only to prevent JavaScript access
  • Secure in production to ensure HTTPS transmission
  • Same-site lax to prevent CSRF attacks
  • Limited 10-minute expiration

This implementation follows OAuth 2.0 security recommendations.


7-10: Proper error handling for missing OIDC configuration

The code correctly checks for the presence of the OIDC authorization endpoint and redirects to an error page if not configured, preventing runtime errors.

src/lib/services/user-service.ts (1)

78-81: Early return for users without passwords looks good

The added guard prevents bcrypt.compare from being called with undefined, avoiding a runtime type error for OIDC-only users. ✅

src/routes/settings/tabs/authentication.svelte (1)

30-32: Verify $derived usage compiles under your Svelte setup

$derived() is not a standard Svelte macro. Unless you are using the “@svelte-put/state”/“$macro” plugin (or similar) this will raise a compile-time error.
If you only need a reactive declaration, the idiomatic Svelte syntax is:

$: isOidcViewMode = data.oidcEnvVarsConfigured;

Please make sure your tool-chain supports the $derived helper.

@kmendell kmendell mentioned this pull request May 12, 2025
3 tasks
@kmendell kmendell merged commit 43609a8 into main May 13, 2025
5 checks passed
@kmendell kmendell deleted the feat/oidc-login branch May 13, 2025 03:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

⚡️ Feature: Add OIDC/SSO option

1 participant