Skip to content

spec: Specify DCR, Audience Binding, Third Party App#5750

Open
tung2744 wants to merge 4 commits into
authgear:mainfrom
tung2744:dev-3608
Open

spec: Specify DCR, Audience Binding, Third Party App#5750
tung2744 wants to merge 4 commits into
authgear:mainfrom
tung2744:dev-3608

Conversation

@tung2744
Copy link
Copy Markdown
Contributor

@tung2744 tung2744 commented Jun 2, 2026

ref DEV-3608

Comment thread docs/specs/dcr.md
Comment thread docs/specs/dcr.md Outdated
Comment thread docs/specs/dcr.md
Copy link
Copy Markdown
Member

@chpapa chpapa left a comment

Choose a reason for hiding this comment

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

@tung2744 some after thought after yesterday discussion.

Comment thread docs/specs/dcr.md Outdated
Comment thread docs/specs/dcr.md Outdated
Comment thread docs/specs/dcr.md
Comment thread docs/specs/access-token-audience-binding.md Outdated
@chpapa

This comment was marked as resolved.

@tung2744

This comment was marked as resolved.

@chpapa

This comment was marked as resolved.

@tung2744

This comment was marked as resolved.

@chpapa

This comment was marked as resolved.

@tung2744

This comment was marked as resolved.

@chpapa

This comment was marked as resolved.

@tung2744

This comment was marked as resolved.

@tung2744

This comment was marked as resolved.

@chpapa

This comment was marked as resolved.

@tung2744

This comment was marked as resolved.

- Define third-party client concept; deprecate static third_party_app YAML entry
- Extract API Resources and Scopes from m2m.md into api-resource.md; add allow_any_client_access flag
- Revise access token audience binding: opaque token by default for third-party clients; aud=[resource_uri] only when resource is specified
- Change IAT from self-signed JWT to opaque token issued via Admin API; remove single-use restriction
- Redesign DCR application_type: web/native for third-party, first_party_spa/native/traditional_webapp/confidential for first-party
- Remove token_endpoint_auth_method from DCR; fixed per application_type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tung2744
Copy link
Copy Markdown
Contributor Author

tung2744 commented Jun 4, 2026

Updated. Here is a summary:

Third-Party Clients

  • Redefined third party client by listing out difference with first party client
  • The existing x_application_type: third_party_app static YAML entry is deprecated; new third-party clients must be created via DCR and never exist in authgear.yaml
  • By default (no resource parameter), third-party clients receive an opaque access token (no aud, userinfo only, not usable with /resolve)
  • When resource is specified, a JWT is issued with aud = [resource_uri] only — the project endpoint is not included

API Resources and Scopes

  • Extracted from m2m.md into a dedicated spec (api-resource.md)
  • Added allow_any_client_access flag at both Resource and Scope level — when set on both, any client (including third-party DCR clients) can access that resource/scope without a per-client association
  • Resource access for DCR clients is configured in the portal, not via authgear.yaml (allowed_resources config removed)

Access Token Audience Binding

  • First-party clients retain existing behavior: JWT with aud = [project_endpoint] when no resource is specified
  • When resource is specified, aud contains only the resource URI — the project endpoint is never included (applies to both first-party and third-party)
  • Removed the per-client URI (project_endpoint/clients/client_id) in previous version

Initial Access Token (IAT)

  • IAT is now an opaque token issued via the Admin API, replacing the previous JWT-based design
  • Admin API mutations added: create, revoke, and list IATs

DCR application_type Field

  • Conforms to the OIDC DCR spec: web and native are the standard values, both creating third-party clients
    • web: HTTPS redirect URIs required, no localhost
    • native: custom URI schemes or http://localhost
  • Added Authgear extension values for first-party clients: first_party_spa, first_party_native, first_party_traditional_webapp, first_party_confidential — all require an IAT
  • Default is web
  • x_application_type is not modified or renamed. It only appears in static clients in authgear.yaml and does not conflict with standard application_type which is in DCR which are in database.

DCR token_endpoint_auth_method Field

  • Field removed from DCR — the auth method is now fixed by application_type. I think we do not need to support token_endpoint_auth_method.

@chpapa
Copy link
Copy Markdown
Member

chpapa commented Jun 5, 2026

@tung2744 haven't read the doc in details yet, just responding on summary:

* The existing x_application_type: third_party_app static YAML entry is deprecated; new third-party clients must be created via DCR and never exist in authgear.yaml

I still think we want to support creating 3rd party client application from portal (how to store it is another thing), but feature-wise "3rd parties client must be created via DCR" seems incorrect. (Implementation could be another ticket though)

* Added allow_any_client_access flag at both Resource and Scope level — when set on both, any client (including third-party DCR clients) can access that resource/scope without a per-client association

What's the intention on this?

* When resource is specified, aud contains only the resource URI — the project endpoint is never included (applies to both first-party and third-party)

For first-party client, do we really want to do this? or we allow them to specify ....?

And I assume even if you do this, we allow multiple resources + project endpoint at authorize but just tokens return 1 aud for 1 token?

* Added Authgear extension values for first-party clients: first_party_spa, first_party_native, first_party_traditional_webapp, first_party_confidential — all require an IAT

As previously commented, maybe we should have first_party vs 3rd_party as a attribute instead of app type? By making it app_type, we seems overloading a lot of attributes that could potentially conflict with other oidc spec on it...

@tung2744
Copy link
Copy Markdown
Contributor Author

tung2744 commented Jun 5, 2026

@chpapa

I still think we want to support creating 3rd party client application from portal (how to store it is another thing), but feature-wise "3rd parties client must be created via DCR" seems incorrect. (Implementation could be another ticket though)

What I want to do is to deprecate the existing third_party_app in the config. I believe we can define another way to add third party app in config, or call DCR to register in portal. But I want to avoid designing this part in this spec. I think it is easy to add in the future.

What's the intention on allow_any_client_access

It is the same purpose as allowed_resources in the initial design. Now we put this config on the api resources & scopes. It allows any client to request access to that resource.
Maybe you would ask, why is it "any client" but not third party client?
My reasons:

  1. In UC1 which first party client is registered through DCR, if the project owner wants to use resource indicator, this will allow him to do so.
  2. This design ensures third party client is always a subset of first party client (There should not be anything third party client can do but first party client can't).

For first-party client, do we really want to do this? or we allow them to specify ....?

I assume you mean we do aud=[API_RESOURCE] but not aud=[AUTHGEAR_ENDPOINT, API_RESOURCE].
My reasons:

  1. Basically the reason of using resource indicator is to narrow down the target audience. By using resource=<API_RESOURCE>, the user already tell us the intended consumer of the access token is API_RESOURCE.
    • The resource server (API_RESOURCE), can just verify the token with aud=[API_RESOURCE], seems no reason to include AUTHGEAR_ENDPOINT.
  2. If you are asking about why it is not for third party client only: In my opinion resource indicator should behave the same way in first or third party client. The resource parameter itself is already optional. If the developer do not want to narrow down the audience, just do not use it. IMO, in this way the resource parameter is also less confusing.

And I assume even if you do this, we allow multiple resources + project endpoint at authorize but just tokens return 1 aud for 1 token?

  1. I do not plan to allow using <AUTHGEAR_ENDPOINT> in resource parameter. This should only be the "default" audience when resource is not provided.
  2. Multiple resources in authorize endpoint is allowed, as OAuth spec mentioned.
  3. Multiple resources in token endpoint is also allowed. Reading https://www.rfc-editor.org/info/rfc8707/#name-access-token-request, although there isn't an example for it, it is actually allowed.

    When requesting a token, the client can indicate the desired target service(s) where it intends to use that token by way of the resource parameter and can indicate the desired scope of the requested token using the scope parameter. The semantics of such a request are that the client is asking for a token with the requested scope that is usable at all the requested target services.

    • Although, for first party client, the exact mechanism for authgear to decide what resource or scope is allowed is still undefined. I believe that should be another feature. As a result, for now, first party app cannot really use resource parameter - We will always reject the request.

As previously commented, maybe we should have first_party vs 3rd_party as a attribute instead of app type? By making it app_type, we seems overloading a lot of attributes that could potentially conflict with other oidc spec on it...

I would like to clarify to ensure we are talking about the same thing, the application_type here is referring to the parameter of the DCR /register endpoint.

We actually have two choice here:

  1. Add additional options for the application_type parameter for first party app.
  2. Add additional parameter on the /register endpoint, e.g. x_is_first_party.

Now it is 1. I did not do 2 because:

  • It creates ambigious / invalid combinations. Like application_type=web, x_is_first_party=true. Should it be mapped to traditional_webapp or spa? Sure we can define a mapping in doc or do not allow registering traditional_webapp from DCR, and also block all invalid combination. But isn't a single application_type parameter simpler? We do not need to handle x_is_first_party * application_type * token_endpoint_auth_method combinations if application_type already handle all of them.
  • I think it is easier to find the well known application_type parameter than a custom x_is_first_party parameter.
  • I think the risk of value collision on first_party_xxx is low. If we want to be safe, we can further rename to x_first_party_xxx.

@chpapa
Copy link
Copy Markdown
Member

chpapa commented Jun 5, 2026

What I want to do is to deprecate the existing third_party_app in the config. I believe we can define another way to add third party app in config, or call DCR to register in portal. But I want to avoid designing this part in this spec. I think it is easy to add in the future.

ok, but than we probably shouldn't say DCR is the only way for 3rd party app.

It is the same purpose as allowed_resources in the initial design. Now we put this config on the api resources & scopes. It allows any client to request access to that resource.
Maybe you would ask, why is it "any client" but not third party client?

I think this is werid and not obvious for client what this attributes does. We need both a better naming and description. (More specific for DCR open registration

I do not plan to allow using <AUTHGEAR_ENDPOINT> in resource parameter. This should only be the "default" audience when resource is not provided.

Than what if a 1st party app want to authorize for <AUTHGEAR_ENDPOINT> + resourcesA (and call multiple /token to get tokens for each audience)

We actually have two choice here:

  1. Add additional options for the application_type parameter for first party app.
  2. Add additional parameter on the /register endpoint, e.g. x_is_first_party.

Actually I was thinking more like:

  1. For static app, the client_id have an attribute to define is it a 1st party or 3rd party, so the client_id decide it.
  2. For DCR app, the type of IAT define it.

I haven't thought through which approach is better. Yet I feel like the above seems less dangerous than what you proposed / we originally have (creating more application_type to signal it):

  1. Actually I'm suggesting we should even remove the internal x_application_type thing, so we should just say app_type = web + auth_method = none is SPA; auth_method = client_secret_basic is web

  2. So we don't run into, as you said, what if the app_type and auth_method attribute contradict

(and we don't run into similar confusion when we need to adopt new OIDC protocol like this time)


BTW, it seems in your summary you didn't mention IAT for 1st party app vs 3rd party app, so how to limit it for now? Or IAT is always for 3rd party app only and 1st party app (UC1) is for future work?


See if you have think through all of the above, and let me know when you think I should read the whole spec again but not just your summary? (Yet I think via the summary we can discuss and align some design principle / use cases clarity)

p.s. I would be on leave til next Wed, maybe the best time to review it again is next Wed morning (or pls schedule with me separately.)

@tung2744
Copy link
Copy Markdown
Contributor Author

tung2744 commented Jun 5, 2026

  1. Than what if a 1st party app want to authorize for <AUTHGEAR_ENDPOINT> + resourcesA (and call multiple /token to get tokens for each audience)

Do not allow. Reason:

  • If you do not need audience isolated token, validate aud=[AUTHGEAR_ENDPOINT].
  • If you want audience isolated token, validate aud=[API_RESOURCE].

There is also no reason for putting AUTHGEAR_ENDPOINT in resource parameter in authorize endpoint, because authgear server always accept access token issued by itself. (As discussed we will relax the aud requirement of userinfo endpoint).

So for this case just request resource=API_RESOURCE in both authorize and token endpoint.

  1. allow_any_client_access

Let me also think if we can have a better naming for allow_any_client_access.

  1. About application_type

Actually, I am trying to make it unrelated to the static config (And thus, unrelated to x_application_type).

Removal of x_application_type seems to be a redesign of our existing oidc client. I prefer not to do it here. There are quite many logic related to this field such as what portal configs are available, and how the clients are created, not only related to first/third party and client secret (m2m is actually also a x_application_type). I feel like this PR is already too many things.

So I suggest just to treat application_type a parameter of DCR. Map each possible DCR request to an existing x_application_type for now (If it is a first party app, new third party app has no relationship with x_application_type). Reject all other invalid combinations. In this way, migrate existing first party x_application_type to another model which compatible to all existing dynamic clients should be still possible.

I see some other services also overloading application_type, e.g. Okta

But it is indeed not very common.

If overloading application_type feels dangerous, I suggest we just add a new parameter x_is_first_party. And a mapping:

  • x_is_first_party=true & application_type=web, map to x_application_type=spa of static client.
  • x_is_first_party=true & application_type=native, map to x_application_type=native static client.
  • x_is_first_party=false & application_type=ANY, new third party client. application_type not stored and only affect validation of redirect uris.

For DCR app, the type of IAT define it.

I think the purpose of IAT should be limited to authorization. It can authorize the caller to create first party client, but whether to create a first party client should still be decided by the caller and be reflected in the request.

  1. About IAT for first / third party app

I think no change from the initial design. If there is an IAT, first party app creation is allowed. Third party app is allowed no matter you have a IAT or not.
You have a config initial_access_token_required to choose whether the registration endpoint is open or not.
Limitation per token (Such as number of client can be created, rate limits..) can be applied later because now the token is opaque and must be generated using an api. But I did not define what can be controlled in this version yet.


I think you have already get most of the idea from my summary. See if the above answers your question, or we further discuss later.

@chpapa
Copy link
Copy Markdown
Member

chpapa commented Jun 5, 2026

Let me also think if we can have a better naming for allow_any_client_access.

Just wanna stress that I think it is not just naming, but the concept behind...

If overloading application_type feels dangerous, I suggest we just add a new parameter x_is_first_party. And a mapping:
I think the purpose of IAT should be limited to authorization. It can authorize the caller to create first party client, but whether to create a first party client should still be decided by the caller and be reflected in the request.

I would strongly oppose this. An IAT that can create 1st party vs 3rd party application have vastly difference security profile. In fact I always think the only decision for us is either DO NOT allow register of 1st party application via DCR at all (drop UC1), or the bottom line is make it explicit which IAT can do so.

(btw, hence it is better if we have some prefix on the IAT token, like prod vs test token in Stripe, so it is very obvious to users they're leaking a sensitive or non-sensitive tokens, e.g. ag_iat_1st_xxxxxxxx lol)

Since the decision of 1st party or 3rd party app is by type of IAT, hence the parameter of x_is_first_party is not a must (unless we want to let 1st party IAT user express this intent, but seems a bit unnecessary flexible)

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.

3 participants