WebUI OIDC login uses a hardcoded client_id (stalwart-webui) — incompatible with per-client-JWKS IdPs (e.g. Kanidm)

Issue Description

I run an external OpenID Connect directory (type = “oidc”) whose issuerUrl points at a specific Kanidm OAuth client (https://auth.example.org/oauth2/openid/mail). Mail-protocol authentication (webmail via JMAP, and IMAP/SMTP OAUTHBEARER) works perfectly against this directory.

I expect the web interface (/account) SSO login to work against the same external IdP: after the user authenticates and Stalwart exchanges the authorization code, the returned token should be validated and an authenticated web session should be established.

Relevant docs: OpenID Connect backend.

Expected Behavior

The webadmin OIDC login completes: Stalwart validates the token returned from the IdP and establishes an authenticated /account session for the user.

Actual Behavior

The login flow reaches the IdP and the code exchange succeeds (the IdP returns HTTP 200 on /oauth2/token), but Stalwart fails to validate the returned token and falls back to HTTP Basic auth.

The root cause is a client/key mismatch: the webadmin redirects to the IdP with a hardcoded client_id=stalwart-webui, but Stalwart validates the returned token against the directory’s JWKS (the issuerUrl configured for the OIDC directory, e.g. …/openid/mail). With IdPs that sign each OAuth client’s tokens with a distinct key (Kanidm scopes issuer and JWKS per client: …/openid//public_key.jwk), the stalwart-webui token is signed by a key that is absent from the mail client’s JWKS → validation fails.

This works with IdPs that expose a single shared JWKS across all clients (Authentik, Keycloak), but not with per-client-JWKS IdPs like Kanidm. There is no setting to change the webadmin client_id to the one the directory uses, nor to validate the webui token against its own iss/JWKS.

Request: make the webadmin OIDC client_id configurable (so it can reuse the directory’s client), or validate the webui token against the token’s own iss/JWKS (from OIDC discovery of the token issuer) rather than the directory’s issuerUrl

Reproduction Steps

  1. Configure an external OIDC directory and set it as the default (Authentication.directoryId + the default domain’s directoryId):

    directory."idp".type       = "oidc"
    directory."idp".issuer-url  = "https://auth.example.org/oauth2/openid/mail"   # a specific IdP client "mail"
    directory."idp".require-audience = "mail"
    directory."idp".claim-username   = "email"
    
    
  2. On the IdP (Kanidm), register the webadmin client Stalwart expects: a public/PKCE client named stalwart-webui with redirect URI https://mail.example.org/account/oauth/callback. (Kanidm signs this client’s tokens with a key distinct from the mail client’s key.)

  3. Open https://mail.example.org/account, choose SSO login, authenticate at the IdP and consent.

  4. Observe: the browser returns to /account/oauth/callback, then Stalwart challenges HTTP Basic auth instead of establishing a session.

Stalwart Version

v0.16.x

Installation Method

Built from source

Database Backend

PostgreSQL

Blob Storage

S3-compatible

Search Engine

Internal

Directory Backend

Internal

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Your diagnosis matches how the WebUI’s OIDC login is documented to work. Two relevant facts from current docs/source:

  1. The WebUI’s OAuth client_id is a build-time setting, not a runtime server config. It’s controlled by the VITE_OAUTH_CLIENT_ID environment variable used when building the WebUI (defaults to stalwart-webui) — see the WebUI README. There is no field in the OidcProvider or Directory objects that lets you change the client_id the WebUI presents at login; it’s hardcoded in webui/src/services/auth/oauth.ts unless you rebuild with a different VITE_OAUTH_CLIENT_ID. So a workaround exists (build the WebUI yourself with VITE_OAUTH_CLIENT_ID=<your-mail-client-id>), but there’s no way to set this from the server config UI.

  2. Token validation against an OIDC directory is scoped to that directory’s configured issuerUrl, not derived dynamically from the token’s own iss claim via a fresh discovery lookup. Per the OIDC backend docs, Stalwart resolves signing keys from the discovery document at the directory’s issuerUrl and validates tokens against those keys/audience. There’s no documented option to validate against the token’s own issuer/JWKS independently of the directory’s issuerUrl.

So the behavior you’re seeing (webadmin authorization code exchange succeeds, but token validation fails against the mail directory’s per-client JWKS) is consistent with the current design: there’s no configuration to align the WebUI’s client_id with the mail directory’s client, and no way to validate against the token’s own JWKS separate from the configured directory. Rebuilding the WebUI with a matching VITE_OAUTH_CLIENT_ID is the only workaround visible in the docs today.

This is a legitimate feature request; a human maintainer will need to weigh in on whether making the WebUI client_id server-configurable (or validating tokens by their own iss) is planned.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

The WebUI is a self-contained SPA and for that reason it does not support customizing its client id. Ask Kandim to support configuring client ids, other IdPs support it.

I believe OpenCloud had a similar issue which they solved by making the server serve an endpoint which returns the client-ids for their public clients. (see https://github.com/opencloud-eu/desktop/issues/811).

And if my interpretation of the spec is correct then the spec says that it’s the IdPs job to assign client-ids: https://www.rfc-editor.org/rfc/rfc6749.html#section-2.2 ?

Once CIMD is implemented in most IdPs the WebUI will start using it instead of a static client-id.

As previously mentioned to Stalwart folks, Kanidm supports setting an arbitrary client ID (within some limits). :wink:

It is not the hard coded client ID that is a problem, it is that Stalwart assumes that multiple client IDs in a single IdP have the same iss.

Kanidm is always multi-tenant-like (per OpenID Connect Core 1.0 §16.15), where each client application is in its own tenant (and thus unique iss). This improves security by preventing unintended cross-client token re-use, with unique iss, JWKS and endpoint URLs.

Unfortunately, this issue is because Stalwart seems to rely on insecure cross-client token reuse.

If a backend needs to authorise an SPA, that should be handled by the backend, using a BFF. If an SPA handles its own tokens (even re-using its backend’s public client configuration), any script injection vulnerability allows token theft (further risks are outlined in “OAuth 2.0 for Browser-Based Applications”).

It is possible to make a “zero config” SPA which can authenticate with a BFF flow, provided that the SPA has some way to determine the backend’s API URL (which I assume for Stalwart, it already does).

Implementing CIMD will not fix this issue, because it will still be a different iss. CIMD has other problems which make it not suitable without pre-registration, but that’s another topic.

@micolous Thanks for the clarification. Based on the user reports we had assumed this was mostly about the hardcoded client_id, and your explanation of the per-client iss model makes it clear that the client id is only the surface of it.

The bigger issue is that this is not really a WebUI problem. Stalwart’s OAuth support was designed around the OAuth Profile for Open Public Clients:

That profile is what lets a mail client be pointed at any server and just work, with no per-client setup by an administrator anywhere. Stalwart already implements the server side of it. The problem is that the profile and Kanidm’s design are incompatible in several places at once, not just one:

  • Dynamic Client Registration (RFC 7591) is mandatory. The profile’s flow is fetch metadata, register, then authorize, and registration_endpoint is a required metadata property. Kanidm returns registration_endpoint: None, and from #4095 it sounds like that is a settled position rather than a gap.
  • token_endpoint_auth_methods_supported must include none. Kanidm advertises client_secret_basic and client_secret_post.
  • grant_types_supported must include authorization_code and refresh_token. Kanidm advertises authorization code and token exchange.
  • authorization_response_iss_parameter_supported must be true, and the client is required to verify the iss returned in the authorization response (RFC 9207) as mix-up protection. Kanidm does not advertise this and returns only code and state in the redirect.
  • The profile presumes one authorization server issuer identifier shared by all of a deployment’s resource endpoints (JMAP, IMAP, SMTP, POP, CalDAV, CardDAV). Kanidm’s issuer is per client.

That last point is also why CIMD would not rescue this, as you already said in the other thread, and it is why DCR and per-client issuers cannot really coexist: the profile requires the issuer in the metadata to be identical to the issuer the metadata URL was derived from, and requires the iss in the authorization response to match that same value. A dynamically registered client that got its own issuer would fail both checks by construction. On top of that, the resource server has to publish its authorization server in RFC 9728 protected resource metadata, and it cannot name an issuer that does not exist until after a client registers.

I would encourage the Kanidm developers to look at this seriously, because it is about to stop being a Stalwart-specific problem. There is a companion draft in the same working group, PACC, for automatic configuration of mail, calendar and contact settings:

Two of its three authors are from Apple, it is on the Proposed Standard track, and it takes OAuth Public as a normative reference: “If OAuth is used, the OAuth authorization server MUST adhere to [OAuthPublic]”. Its autoconfig payload carries a nested oauth-public object with a single issuer value, so the format itself has no way to express a different issuer per client. I participate in the IETF work and Apple has already implemented this in Apple Mail, it simply has not shipped yet. So this is not a hypothetical: Apple Mail will use OAuth Public, and every client that implements PACC gets OAuth Public along with it, which in practice means most modern mail clients before long.

To be precise, none of this breaks the existing “legacy” authentication. Clients using conventionally pre-registered OAuth, or app passwords, keep working exactly as they do now. What breaks is the new class of clients that expect to be pointed at a server and configure themselves, and that class is going to become the default rather than the exception.

If the Kanidm developers think the draft is wrong, or that A-DCR and CIMD are the wrong mechanisms, the place to say so is the mailmaint working group list ([email protected]), and the time is now. The draft has been described on the list as essentially finished and waiting on implementation experience before being pushed toward publication. Once it is published and clients ship it, the discussion moves from “should the draft require this” to “Kanidm does not implement the thing every mail client now expects”, and at that point the only options left are changes on the Kanidm side. The objections raised in Kandim’s issue #4095 about PII disclosure, scope mapping and client spam are real concerns and they deserve to be heard by the people writing the spec, rather than being resolved by everyone independently discovering that their mail client no longer connects.

Hi there, I’m the lead developer of Kanidm so I thought it’d be good to jump in and explain some stuff.

First up, we are aware of the upcoming OAuth2 Public draft specification. I plan to review it again so currently I have no major comments to make about it, but I do understand that dynamic client registration is a core part of it’s operation. Until I sit down to implement it proper I probably won’t have any feedback.

I think there are kind of two core issues we’ve had with stalwart here.

The use of a hardcoded client_id is the first one, but since users can just register that name it’s salvagable. In generally I’d actually argue that the public client should be fetching the metadata via RFC 8414: OAuth 2.0 Authorization Server Metadata | RFC Editor using an issuer name with the well-known uri so that the correct metadata is always retrieved.

The second is how our issuer identifier is different between OAuth2 clients. This is arguably correct if you read between the lines of the same section of the above rfc. Part of the reason why users of Kanidm today have to register multiple clients here though is due to the hardcoded client ids which then naturally causes there to be distinct issuers. Importantly Kanidm separates clients into their own domains, each with unique cryptographic material and keys. That’s probably what trips up most users/deployments is many other IDPs don’t take such a strict view on client isolation.

Both of these are would be resolved with OAuth2 Public - stalwart will need to implement correct metadata fetching in it’s SPA removing the need for client_id hardcoding. And in Kanidm if we implement OAuth2 Public will have to find a way to implement DCR such that multiple clients share an issuer.

For a long time the issue with DCR was “how to make it secure”. It’s easy to just implement DCR, but it’s also similarly easy to implement it in a way that can cause problems. This is why for a long time we’ve been against implementing it (and by derivation, against OAuth2 Public) but recently we have devised a method to make a secure implementation that meets our projects standards. Once we have DCR, that would be the major part of OAuth2 Public implemented. Within our implementation, DCR clients within a “client domain” will share the same issuer, so that resolves another of the major issues you have.

So to address the last two small details, the missing grant_types and incorrect token_end_point_auth was an oversight - I’ve put in a PR to fix both.

PS: What is A-DCR - it’s not referenced in any specification I can find.

EDIT: Formatting.

Hi @firstyear,

Just wanted to make a few clarifications, mostly because I do not think we explained the WebUI side well enough.

The hardcoded client id is not a Stalwart server issue. The server has no hardcoded client id anywhere, it accepts whatever the directory is configured with. The problem is entirely in the WebUI, which is a single page application, and the client id is a compile time constant baked into the JavaScript bundle at build time. Changing it means rebuilding and redeploying the WebUI, which is not something we can reasonably ask an administrator to do just to point it at their IdP. The real fix in the longer term is CIMD, where the client id becomes a URL that the SPA can derive from its own origin, so there is nothing left to configure or hardcode at all. To be clear, CIMD solves the hardcoded client id problem, it does not by itself solve the per client issuer question, which @micolous already pointed out above.

On metadata fetching, Stalwart already does this. The server fetches the authorization server metadata from the configured issuer and hands the resulting endpoints to the SPA (and other OAuth clients), so the SPA never guesses an endpoint. The only thing it supplies itself is the client id. And this is where the suggestion to fetch metadata from the issuer well known URI does not quite close the loop for Kanidm specifically, because with per client issuers the issuer URL contains the client id. You cannot fetch the metadata until you already know which client you are, so metadata fetching cannot be what removes the need to know a client id. For every other IdP the two are independent and the suggestion works fine.

On multi tenancy, Stalwart does support it. You can configure a different directory, and therefore a different OIDC discovery document and a different issuer, for each domain the server hosts. What we cannot do is offer a different discovery document per client, and that is not a limitation we chose. PACC binds a domain to a single authorization server: its configuration payload carries a nested oauth-public object with one issuer value for the whole domain, and there is no way to express a different issuer for a different client. OAuth Public makes the same assumption from the other direction, presuming a common authorization server issuer identifier for all of a deployment’s resource endpoints in section 1.1, and requiring in section 1.2 that those endpoints publish RFC 9728 protected resource metadata naming the authorization server. In every one of these the issuer is a property of the service or the domain, and it is resolved before any client exists. A mail client discovering an account from an email address has no way to say which client it is before it has an issuer, because obtaining the issuer is the first step. That ordering is what makes per client issuers unworkable for autoconfiguration, independently of whether per client issuers are a good idea in general.

Sorry for A-DCR, I meant anonymous dynamic client registration. What I meant is that OAuth Public requires registration with no client credential and no initial access token, so any client can register unprompted, and PACC inherits that requirement because it says that if OAuth is used the authorization server MUST adhere to OAuth Public. That is the property I expected to be contentious for Kanidm given the previous discussion.

One thing in the draft that may help you here, is that Section 3.3 says that servers “MAY choose to ignore all of the information instead and just return a static client id to all requests”. Nothing obliges an authorization server to create a persistent record per registration. That looks compatible with your client domain approach, since you could expose a registration endpoint per client domain that returns a fixed client id belonging to that domain, which would give you conformance without unbounded client creation and without giving up your isolation boundary. It also means the spam concern is addressable within the spec rather than being an argument against it.

Finally, on issuer identifiers being different per client being arguably correct, I would only note that OIDC Core section 16.15 recommends a single issuer per host and frames multiple issuers as something needed when a host supports multiple tenants, so it permits the mechanism but describes it at tenant granularity rather than client granularity. That is a matter of interpretation and I do not think it is the important part. The operative constraint is the ordering problem above, which applies regardless of how that section is read.

In any case, if you do build the secure DCR design you described, it would be well worth bringing to the mailmaint list. The security properties of anonymous registration are exactly the kind of implementation experience the draft is waiting on, and the authors would rather hear it before publication than after.

You’ve posted a lot of text there, with a writing style that appears to be LLM generated. I spend a lot of time writing my messages to be read by a person, I don’t send you LLM generated text, and I don’t like talking to clankers. Thanks. :slight_smile:

Your first message identified a couple of errors in Kanidm’s OAuth 2.0 Authorisation Server Metadata, which are being addressed.

However, I don’t see how automatic email configuration had anything to do with Stalwart’s present SPA.

As you’ve noted, all of those are draft specs. When we know about them, have reviewed them, and think they’re potentially useful, we’ll send feedback. But there are many more people writing (and increasingly, machine-generating) specs than there are us, so we can’t review everything. There are also some widely deployed and adopted specifications where the specification’s authors send all feedback from outside their clique to /dev/null. :upside_down_face:

But otherwise, I think that Firstyear has covered the other points in your first message, and I’ll let him to go into detail on the second one.

In other words, the Stalwart server (a confidential client) supplies its authentication provider configuration to the Stalwart SPA (a different, public client), under the assumption that most of the configuration that is valid for itself is also valid for another client (minus the client ID).

This is an incorrect assumption, but it is an easy one to fix (ie: /api/spa-config.json).

However, when I was investigating, I discovered a security vulnerability! I’ll email you that privately tomorrow. :slight_smile:

The better way for this to work is for the Stalwart server to act as a OAuth BFF (which I linked previously), so that the SPA doesn’t touch OAuth tokens at all, only HttpOnly cookies. The Stalwart server can even do this on the same endpoints that support native/mobile clients (which send OAuth2 Bearer headers).

I’m aware of this recommendation.

As you have discovered in our commentary on DCR, Kanidm is “opinionated” - it doesn’t support protocols (or parts of protocols) which are insecure, unclear, or unsafe. Sometimes this doesn’t translate well across text, but we’re always open to reasonable, alternative solutions, or new information.

Single-issuer-per-host is an assumption that is unsafe, due to cross-client token reuse.

If you implemented 100% of OAuth 2.0 and OIDC to the letter of the specs, you would end up with an insecure and broken mess.

There are many RFCs and best-practice guides that were released after OAuth 2.0 and OpenID Connect’s initial releases which address these issues. Kanidm tends to be ahead of the curve on these things, requiring things by default that are only officially mandatory in OAuth 2.1.

We seem to be going in circles here. As we mentioned before, Stalwart’s WebUI is an SPA with a hardcoded client id and this can’t be changed/fixed until CIMD is widely available. In addition to that, other IdPs such as Keycloak work well with our WebUI so we do not consider using a hardcoded client id to be an issue.

Regarding your comments about OAuth in general, you don’t have to convince us. Even if you’re right and Kandim is the most secure IdP out there, the reality is that it can’t work with the PACC and OAuth Public drafts as it is. Stalwart only implements IETF standards and we won’t make any workarounds to interact with Kandim (or any other third party products) that do not conform to the specs.

We don’t claim to be OAuth experts but we do try to follow the specs. If you don’t agree with how Stalwart does OAuth, you’re most likely not agreeing with PACC/OAuth public’s design so I suggest you post a message to the mailmaint mailing list explaining your reasons.

PS: and yes, we use LLMs to polish our replies because many of us are not English speakers. It doesn’t mean that the content posted here was not reviewed by a person.

I agree with you there! :slight_smile:

It already fetches other information about the authorisation server on login, using a Stalwart-specific API:

The DiscoveryResponse could also include the client ID, and can be used here:

That’s all fixable in the way I previously suggested, and doesn’t need CIMD.

It also looks like that the SPA does not provide a resource parameter in the authorisation request (RFC 9068 §3), so that should be passed in the DiscoveryResponse as well if there is an expectation of a specific aud.

This isn’t an issue for Kanidm, because you can set a client ID to stalwart, as long as there is no expectation of reuse between multiple clients, or reuse as both a confidential and public client.

This might be an issue for other IdPs.

RFC 6749 §2.2 states (emphasis mine):

The authorization server issues the registered client a client
identifier
– a unique string representing the registration
information provided by the client. The client identifier is not a
secret; it is exposed to the resource owner and MUST NOT be used
alone for client authentication. The client identifier is unique to
the authorization server.

The client identifier string size is left undefined by this
specification.
The client should avoid making assumptions about the
identifier size. The authorization server SHOULD document the size
of any identifier it issues.

The authorisation server sets the client ID, not the other way around. The registered client (Stalwart) cannot make any assumptions about its content or size, or expect availability of a specific client ID.

I’m aware, but I don’t think that PACC and OAuth Public are relevant or necessary for Stalwart’s SPA.

They’ll probably be relevant for other clients in the future, but not the SPA today. :slight_smile:

Every issue I can see with Stalwart’s OIDC/OAuth2 implementation at present is because it makes assumptions based on what other IdPs do.

As another example, it assumes that if an access token can be parsed as a JWT (RFC 9068), that it can be validated using a JWKS. If that fails (eg: encrypted JWT, per RFC 7519 Appendix A.1), there is no fallback to “opaque” mode.

I’m not debating those specs.

Its output has a condescending tone and verbosity that is unpleasant to read. I’d rather read someone’s broken English. :slight_smile:

Thanks!

WebUI, which is a single page application, and the client id is a compile time constant baked into the JavaScript bundle at build time.

Sorry this still is a Stalwart/SPA issue.

The correct flow is the client retrieves the SPA, then requests metadata from the Stalwart server. This then allows the SPA at runtime to dynamicaly use the correct client_id.

As mentioned previously, if you correctly implement OAuth2 Public then this behaviour has to be followed as the SPA will need to retrieve the issuer URI from the Stalwart server, then use the issuer’s well-known address to retrieve the other information which includes the client_id in the RFC8414 metadata bundle. This will be critical as with DCR the SPA must use the client_id issued by the IDP, so you will need to correct this runtime hardcoded client_id behaviour at somepoint.

Sorry for A-DCR, I meant anonymous dynamic client registration.

Better to not make up new terms as they can be confusing. By it’s nature DCR is always anonymous unless you are supplied an Initial Access Token.

“MAY choose to ignore all of the information instead and just return a static client id to all requests”. Nothing obliges an authorization server to create a persistent record per registration.

Redirection URI’s must be strictly validated (can’t remember which RFC that’s in) which means that if a client uses DCR to register we need to persist those redirection URIs for validation. Within OAuth2 Public this may not be an issue as all the URI’s registered will be redirections to localhost, but for DCR in a generic format this is not the case.

Finally, on issuer identifiers being different per client being arguably correct, I would only note that OIDC Core section 16.15 recommends a single issuer per host and frames multiple issuers as something needed when a host supports multiple tenants

The reason I think we have a different view here is potentially how we approach specifications. Many developers (and specification authors) look at a specification and think “how can I achieve my goal?”. We see that in OAuth2, DCR, even OAuth2 Public.

However I approach these specifications with an adversarial mindset. “How will someone try to abuse this?”. “How will a client implementation develop this incorrectly?”. So when I look at these things I am always looking for how it will be broken.

This is precisely why we separate issuers per client - because there are simply too many OAuth2 Clients that do not validate the audience field, and do not validate the client_id or other token related metadata to ensure the token is intended for them. As a result by separating tokens strictly based on the cryptograhic keys used we can ensure that a token for client A is never valid to be used on client B. This prevents a lot of potential attacks against resource servers.

I think this adversarial mindset is critical in when we developer specifications and I think it is probably what guided my feedback to the mailmaint list about the current OAuth2 Public specification as I can already see a number of potential flaws in the document that may lead to issues.

I would also strongly recommend against relying on CIMD. I have reviewed the specification and I do not believe it is possible as an IDP to create a secure implementation.

The SPA is a standalone client which is independent of Stalwart, many deployments don’t even use it at all. For this reason, the server code won’t be changed to make the WebUI configurable. Since the WebUI is a public client, the path forward is making the SPA use CIMD, which is on the roadmap and the IETF blessed way of registering OAuth web clients (when/if it’s standardised, of course).

Also, the WebUI is just one SPA but there will be others in the future (for example the webmail) so we need a mechanism for registering web public clients in general.

And again, it seems like you’re trying to convince the wrong people here. I’m not disagreeing with your technical arguments (except making code changes to the server to make the SPA client id configurable), if you believe CIMD, OAuth Public or PACC are bad or insecure standards please write to the respective authors on the IETF mailing lists. Standards is what separate men from Microsoft :wink:

Edit: Added more details about SPA web public clients.

Given that /api/discover returns a subset of the OpenID Discovery Document, I can understand your hesitancy to deviate from it.

However, another client (like a native mail client) doesn’t know about this API - it is Stalwart-specific. Stalwart has /.well-known documents, but they are for when Stalwart itself acts as an authorisation server, rather than an external identity provider.

Today (ie: in the absence of OAuth Public), those clients are configured with a client ID and the URL to an appropriate OIDC Discovery or OAuth2 Authorisation Server Metadata document. Those endpoints would also provide the complete document, and support things like DCR.

The only type of client that would use /api/discover is one that has no way to be configured with information about the authentication provider in advance, and has been specifically designed to work with Stalwart (ie: the Stalwart SPA).

When using DCR (as proposed in OAuth Public), a client would use a static software_id to register itself. But it needs to store the client_id returned by the authorisation server and use it in all future operations, much like it needs to store the Bearer token for authenticated API calls.

What I’m proposing is functionally similar, but within the bounds of a single, first-party client. This means that Stalwart would at least partially follow the specs. :slight_smile:

If you believe Kanidm’s design is bad, or will be unable to implement some future standard that you’d like to use, please contact the project first (issue tracker, discussions, chat, email), rather than complaining on IETF mailing lists and leaving us to discover your posts by happenstance (when someone asked about CIMD support).

Yesterday I sent you through that security bug, which allows OAuth2 credentials issued to another, non-mail application to be used with Stalwart. This has several preconditions (within specifications) to exploit successfully, but it isn’t exploitable with Kanidm due to strict client isolation.

From reading Stalwart’s code, there seem to be several incorrect assumptions about how OAuth2 works. Outsourcing these to a standards body to enshrine them in a specification is not the way to fix them.

I think we’ve covered everything we need to cover, and are now engaging with those standards groups directly.

Thanks! :slight_smile:

Offering CIMD as an alternative route to conformance gives those implementers a way in.

Minor comment from that IETF thread. Having reviewed CIMD we believe it is worse than DCR, and we likely will not implement it. DCR restricted to localhost only redirections is something we will consider as it can be secured.

Two corrections, and then I will close this out.

The mailing list post you linked is not a complaint about Kanidm. It argues that OAuth Public should support CIMD in addition to DCR rather than instead of it, and one of the reasons I gave for keeping both is that Kanidm’s objections to anonymous DCR are reasonable and that making DCR the only mandatory mechanism would leave servers like yours unable to conform. I was arguing for a route in for you. It is in the message you linked, anyone can read it.

Nor was anyone left to find it by happenstance. I named the working group and the list address twice in this topic, and invited you both to bring your DCR design there, because that is where these drafts are decided.

On the security report: thank you for sending it privately and following the process. It is being handled and you will be credited when the fix ships. No complaints from me about that part.

On the rest, I am done here. Over the course of this topic our replies have been called unpleasant to read, we have been called clankers, and we are now told that our code reveals we do not understand OAuth. Technical disagreement is welcome, and some of your points were useful and have already led to changes on both sides. The tone is not something I am interested in continuing with.

The open questions are DCR, per client issuers, and whether OAuth Public and PACC should accommodate them. None of those get decided in a support forum. I have made my position on the list and I am happy to keep discussing it there, where the draft authors can answer too.

Considering this matter closed.