How /.well-known is your Issuer?

TL/DR: Never have an issuer claim with a path component.

There’s an unintended interaction between the OIDC base specification, the OIDC Discovery specification, and the Well-known URI RFC, one which I think is fascinating because it creates a requirement which many implementors may have missed.

The Problem

Let me set it up for you in three quotes:

REQUIRED. Issuer Identifier for the Issuer of the response.
The iss value is a case-sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components.”

OIDC Core 1.0, Section 2

This statement’s pretty straightforward as it states that the issuer must have a https URL, and might have a path (but doesn’t have to). Further down in the same specification, there’s a recommendation that to have only one single issuer, however that multi-tenant solutions might need more.

OpenID Providers supporting Discovery MUST make a JSON document available at the path formed by concatenating the string /.well-known/openid-configuration to the Issuer. The syntax and semantics of .well-known are defined in RFC 5785 and apply to the Issuer value when it contains no path component.

OIDC Discovery 1.0, Section 4

This statement builds on the previous one by allowing the resolution of an openid-configuration document. What I find interesting here is the second sentence, because it explicitly states that the openid-configuration document may only be resolved if – and only if – the issuer contains no path component at all. Thus, if you are building a multi-tenant solution that uses the issuer, clients are expressly forbidden from resolving the openid-configuration document.

To round this out, here’s a related quote from the Well-Known specification:

Well-known URIs are rooted in the top of the path’s hierarchy; they are not well-known by definition in other parts of the path. For example, “/.well-known/example” is a well-known URI, whereas “/foo/.well-known/example” is not.

RFC 8615 (Obsoletes RFC 5785), Section 3

Why does this matter?

Well, if your customers are largely internal, then you can override the specifications however you want to, and this only matters insofar as you have to document the behavior and suffer a lot of interruptions as your internal colleagues ask you clarifying questions.

If, however, if you expect other organizations, developers, or customers to integrate with your system, you must assume that they’ll be using an off-the-shelf library to drive their authorization; that is, unless you’re willing to provide them with an implementation of your protocol for every language likely to be used by your customers.

In short: Design your system with a single issuer, a single set of token endpoints, and design your multitenancy into your token request.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.