Skip to content

feat: allow deleting the code credential via the admin API - #4592

Draft
walter-hostaway wants to merge 1 commit into
ory:masterfrom
walter-hostaway:feat/delete-code-credential
Draft

feat: allow deleting the code credential via the admin API#4592
walter-hostaway wants to merge 1 commit into
ory:masterfrom
walter-hostaway:feat/delete-code-credential

Conversation

@walter-hostaway

Copy link
Copy Markdown

The admin endpoint DELETE /admin/identities/{id}/credentials/{type} handles
every credential type except code: totp, lookup_secret, webauthn,
oidc, saml, password, and deviceauthn are all removable, but code
falls through to the default branch of the switch in
deleteIdentityCredentials and returns 400 "Credentials type code cannot be deleted." Combined with the fact that removing the sourcing trait does not drop
the derived code credential, there is no way to detach an email/phone code
credential from an identity in place (see #4462).

This change handles CredentialsTypeCodeAuth in the delete switch. Because
code is a first factor only when passwordless code login is enabled, the
existing "last first factor" guard is applied only when
SelfServiceCodeStrategy.PasswordlessEnabled is true; as a pure MFA second
factor code is freely removable, matching totp/lookup_secret. Routing it
through the password/oidc guard unconditionally would wrongly block removal
in MFA-only deployments, where CountActiveFirstFactorCredentials returns 1.

Related issue(s)

Resolves #4462

Checklist

  • I have read the contributing guidelines.
  • I have referenced the issue this change addresses (Removing Trait/Phone does not remove phone number from Credential/Code #4462).
  • I am following the contributing code guidelines.
  • I have read the security policy.
  • I confirm that this pull request does not address a security vulnerability.
  • I have added tests that prove my fix is effective (identity/handler_test.go, TestHandler passing).
  • I have added or changed the documentation.

Further Comments

Design note: the naive fix (routing code through the existing password/oidc
guarded branch) is incorrect for MFA-only setups, where the guard would block
removing a purely-second-factor code. Gating the guard on PasswordlessEnabled
avoids that while still protecting passwordless-code-only identities from losing
their last first factor. Happy to adjust the guard semantics to maintainer
preference.

AI assistance: the analysis, patch, and tests were prepared with Claude Code;
the change is human-reviewed, built (go build ./..., go vet ./identity/), and
tested (go test ./identity/ -run TestHandler).

@CLAassistant

CLAassistant commented Aug 13, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

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.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 543f76f8-1fa7-4f58-b46d-2cb873b522be

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

Comment @coderabbitai help to get the list of available commands.

The admin endpoint DELETE /admin/identities/{id}/credentials/{type}
handled every credential type except code, which fell through to the
default branch and returned 400 "Credentials type code cannot be
deleted." This made it impossible to detach an email/phone code
credential from an identity in place.

Handle CredentialsTypeCodeAuth in the delete switch. Because code is a
first factor only when passwordless login with code is enabled, apply
the last-first-factor guard only in that case; as a pure MFA second
factor it is freely removable like TOTP or lookup secrets.

Resolves ory#4462
@walter-hostaway
walter-hostaway force-pushed the feat/delete-code-credential branch from 7f80a83 to 8408528 Compare August 13, 2026 13:11
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.

Removing Trait/Phone does not remove phone number from Credential/Code

2 participants