Skip to content

bug(auth): OIDC raw-username binding breaks when subject contains ':' #645

@shaun0927

Description

@shaun0927

Summary

In raw-username mode (OIDC_USE_RAW_USERNAME=true), Yuxi stores the OIDC binding placeholder as:

oidc:{sub}:{target_user_id}

but later resolves the bound user by splitting that string on : and reading a fixed segment as the numeric user id.

If the IdP emits a sub that itself contains :, the placeholder no longer resolves back to the real bound user.

Affected code

  • backend/package/yuxi/services/oidc_service.py
    • find_user_by_oidc_sub(...)
    • find_deleted_oidc_user_by_sub(...)
    • _create_oidc_binding_placeholder(...)

Why this is a real bug

The docs describe the placeholder record as the mechanism that preserves the binding between an OIDC identity and a local Yuxi account in raw-username mode.

That mechanism works when sub does not contain :, but breaks when it does.

So this is not only a parsing edge case — it can cause a previously bound account to fail login with a false conflict.

Minimal repro

I reproduced this locally on current main (c47a0192) with the current implementation.

Repro 1: direct binding lookup

  1. Create a local user alice
  2. Create the OIDC binding placeholder for sub = tenant:user
  3. Call find_user_by_oidc_sub(db, 'tenant:user')

Actual result

It returns the deleted placeholder record:

  • resolved_user_id = oidc:tenant:user:1
  • resolved_is_deleted = 1

instead of the real bound user.

Expected result

It should resolve back to the original bound user alice.

User-visible impact

I also reproduced the callback path with a minimal mocked OIDC exchange/userinfo flow.

When the existing bound account uses a sub containing :, the callback redirects to:

/login?oidc_error=OIDC标识已绑定到其他账号,请联系管理员处理绑定冲突

So an already-bound account is treated as if it were bound to a different user.

Root cause

The placeholder format is suffix-based (...:{target_user_id}), but the reader treats it as if every : before the numeric suffix were structural.

That assumption is unsafe for sub values containing :.

Suggested fix direction

A minimal fix would be to keep the existing placeholder format and parse the numeric user id from the right-hand side (for example, right-splitting once), instead of reading a fixed segment after a full split.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions