X Tutup
Skip to content

feat: URL client id for client credentials#2041

Open
elf-pavlik wants to merge 6 commits intoCommunitySolidServer:mainfrom
elf-pavlik:url-client-credential
Open

feat: URL client id for client credentials#2041
elf-pavlik wants to merge 6 commits intoCommunitySolidServer:mainfrom
elf-pavlik:url-client-credential

Conversation

@elf-pavlik
Copy link
Contributor

@elf-pavlik elf-pavlik commented Jun 26, 2025

📁 Related issues

✍️ Description

It allows to use URL as client_id when creating a client credential.
I only uses uuid if no name was provided
If there is an existing credential with provided name it responds with an error - is that a breaking change?
This approach makes sense to me, if one doesn't care about specific name they can just leave it out and get random uuid.
I had to change order of OIDC adapters to try registered client credential before fetching client id
TBH I'm not sure if this is a new feature or a fix or major

✅ PR check list

Before this pull request can be merged, a core maintainer will check whether

  • this PR is labeled with the correct semver label
    • semver.patch: Backwards compatible bug fixes.
    • semver.minor: Backwards compatible feature additions.
    • semver.major: Breaking changes. This includes changing interfaces or configuration behaviour.
  • the correct branch is targeted. Patch updates can target main, other changes should target the latest versions/* branch.
  • the RELEASE_NOTES.md document in case of relevant feature or config changes.
  • any relevant documentation was updated to reflect the changes in this PR.

Copy link
Member

@joachimvh joachimvh left a comment

Choose a reason for hiding this comment

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

If there is an existing credential with provided name it responds with an error - is that a breaking change?

Technically yes, but it seems minor enough that we can cheat and not do a major release for this. Same for the ordering of the adapters changing, should it really be needed.

Alternatively it could also be an option to add an optional flag to the class to change the behavior.

res = await fetch(controls.account.clientCredentials, {
method: 'POST',
headers: { authorization, 'content-type': 'application/json' },
body: JSON.stringify({ name: 'token', webId }),
Copy link
Member

Choose a reason for hiding this comment

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

Was there a specific reason to change this?

Copy link
Contributor Author

@elf-pavlik elf-pavlik Jun 27, 2025

Choose a reason for hiding this comment

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

This one failed https://github.com/CommunitySolidServer/CommunitySolidServer/actions/runs/15914010572/job/44887876671
Due to new error I added: "Token with this name already exists."
This error message itself could include a hint to omit the name for auto generated uuid

Copy link
Member

Choose a reason for hiding this comment

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

This makes me think that perhaps we should be more careful about changing the default behaviour here, as this update would break similar setups that might exist. Additionally, the uniqueness check of the label leaks information about which labels exist of other users, which might be a concern in some situations. So I would suggest adding an optional constructor parameter to enable this behaviour (verbatimLabels?), which defaults to false, so existing setups don't change. The configuration generator could then have another option to enable this (I can do this part after this PR is merged). The uniqueness check then only happens when this parameter is set to true. In case it is set to false the uuid is always added.

Copy link
Contributor Author

@elf-pavlik elf-pavlik Oct 31, 2025

Choose a reason for hiding this comment

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

Maybe it would make more sense to handle uuid based client ids and URL client ids separately. Especially if there was a verification step #2041 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I revereted commit with this change

Comment on lines -7 to +12
"@type": "ClientCredentialsAdapterFactory",
"webIdStore": { "@id": "urn:solid-server:default:WebIdStore" },
"clientCredentialsStore": { "@id": "urn:solid-server:default:ClientCredentialsStore" },
"@type": "ClientIdAdapterFactory",
"converter": { "@id": "urn:solid-server:default:RepresentationConverter" },
"source": {
"@type": "ClientIdAdapterFactory",
"converter": { "@id": "urn:solid-server:default:RepresentationConverter" },
"@type": "ClientCredentialsAdapterFactory",
"webIdStore": { "@id": "urn:solid-server:default:WebIdStore" },
"clientCredentialsStore": { "@id": "urn:solid-server:default:ClientCredentialsStore" },
Copy link
Member

Choose a reason for hiding this comment

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

What is the reason these needed to be swapped? I'm guessing it has something to do with the other adapter trying to fetch the URL label maybe but can you tell me exactly what goes wrong?

Copy link
Contributor Author

@elf-pavlik elf-pavlik Jun 27, 2025

Choose a reason for hiding this comment

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

Without that change ClientIdAdapterFactory was trying to fetch that URL, I don't remember if there was a problem if it didn't exist, but when it was able to fetch that ClientID, it was setting token_endpoint_auth_method to none and complaining that it wasn't allowed and it didn't pick credentials passed via Authorization hearder.

I think it doesn't make sense to use ClientIDAdapter at all when ClientCredentialsAdapter handles that client properly, that's why in the end I changed the order. As you can see all other tests are still passing so it doesn't seem to be affecting anything elese, and if that client hasn't been registered for client credential for that webid there should be no problem.

I haven't tested what happens if that user tries to use the same client both with client credential and with authorization code. Should I add integration test for that?

Copy link
Member

Choose a reason for hiding this comment

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

The ClientIdAdapterFactory was written under the assumption that if the incoming ID is a URL, this is always the class that should handle it, but that breaks here. The "deeper" class always triggers first, so if a client ID is used that is also a credential label the credential adapter will trigger first. But this could potentially cause issues when someone uses a client ID request which happens to also be the label of a credential token (of someone else potentially). So this definitely needs to be investigated what would happen there. I'm not sure if this would work with how the adapters currently work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since only one client credential per Client ID is supported with this approach (#2053 could be an alternative) I think a check similar to linking an existing WebID would make sense here. One would have to prove control over client to get a URL Client ID credential for it. Once such credential is created, it can only be used with it and no more authorization code flow for that client.

});
});

describe('using client_credentials - url', (): void => {
Copy link
Member

Choose a reason for hiding this comment

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

Why was this entire block added as it differs on only one line from the previous block. Is it to check for an issue related to the reason the order of the components was changed?

Copy link
Contributor Author

@elf-pavlik elf-pavlik Jun 27, 2025

Choose a reason for hiding this comment

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

Yes, just changing from string to URL was resulting in a different adapter handing it and failing. I though the safest would be to run the whole describe for both cases to make sure that both string and url are fully functional.

Co-authored-by: Joachim Van Herwegen <joachimvh@gmail.com>
@elf-pavlik
Copy link
Contributor Author

I created a discussion in oidc-provider repo

@elf-pavlik
Copy link
Contributor Author

superseded by #2053

@elf-pavlik elf-pavlik closed this Aug 17, 2025
@elf-pavlik
Copy link
Contributor Author

elf-pavlik commented Oct 31, 2025

Since I didn't find anyone else that would be interested in #2053 and changes here would unblock my immediate need, I think I'd better get this one done and keep looking if anyone else finds use for grant-type:jwt-bearer

@elf-pavlik elf-pavlik reopened this Oct 31, 2025
@elf-pavlik
Copy link
Contributor Author

New approach handles string and URL clientId differently. For string it should be the same as before and for URL it requires ownership validation using the same one as for linking WebID.

Since registering client credential for URL will not allow its use with Authorization Code flow, we could add a small warning box in the dev documentation.

@elf-pavlik elf-pavlik marked this pull request as ready for review November 12, 2025 19:23
let label: string;

if (name && isUrl(name)) {
// Validate owneship first not to leak existence of credentials!
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Validate owneship first not to leak existence of credentials!
// Validate ownership first, so as not to leak existence of credentials!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I will most likely need to add a test case for that so I'll include your suggestion in that commit.

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

X Tutup