Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Changelog

5.2.4 - 2026-07-27
- Added code to warn users if scope is downgraded

5.2.3 - 2026-07-20
- Added code to harden check and disallow update user action

Expand Down
1 change: 1 addition & 0 deletions OAuth2.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ public function getClientSideTranslationKeys(&$translationKeys)
$translationKeys[] = 'OAuth2_AdminTypeHelp';
$translationKeys[] = 'OAuth2_AdminGrantTypesHelp';
$translationKeys[] = 'OAuth2_AdminScopeHelp';
$translationKeys[] = 'OAuth2_AdminScopeReducedWarning';
$translationKeys[] = 'OAuth2_AdminRedirectUrisHelp';
$translationKeys[] = 'OAuth2_AdminActiveHelp';
$translationKeys[] = 'OAuth2_ErrorXNotProvided';
Expand Down
1 change: 1 addition & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"AdminScope": "Allowed scope",
"AdminScopes": "Allowed scopes",
"AdminScopeHelp": "Scope defines the maximum access level granted to the OAuth token. For example, if the %1$sadmin%2$s scope is used, the token will have admin-level access at most, even if the authenticated user is a superuser.",
"AdminScopeReducedWarning": "You reduced the allowed scope, but access tokens already issued keep their previous scope until they expire. To restrict existing access immediately, Pause the client.",
"AdminRedirectUris": "Redirect URIs (one per line)",
"AdminRedirectUrisHelp": "Allowed redirect or callback URIs for the authorisation code flow (one per line, must match exactly).",
"AdminActiveLabel": "Active",
Expand Down
2 changes: 1 addition & 1 deletion plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "OAuth2",
"description": "Provide secure access to the Matomo API using scoped permissions. No static credentials.",
"version": "5.2.3",
"version": "5.2.4",
"theme": false,
"require": {
"php": ">=8.1.0",
Expand Down
37 changes: 35 additions & 2 deletions tests/UI/OAuth2_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ describe("OAuth2Admin", function () {
await testEnvironment.save();
}

async function fillClientForm(name, typeTitle, redirectUri)
async function fillClientForm(name, typeTitle, redirectUri, scopeTitle = 'Matomo read level access')
{
await page.evaluate(function (name) {
$('#name').val(name).change();
Expand All @@ -110,7 +110,7 @@ describe("OAuth2Admin", function () {
await selectValue(page, 'div[name="type"]', typeTitle);
}

await selectValue(page,'div[name="scopes"]', 'Matomo read level access.');
await selectValue(page,'div[name="scopes"]', scopeTitle);
await page.evaluate(function (redirectUri) {
const redirectField = document.querySelector('#redirect_uris');

Expand Down Expand Up @@ -204,4 +204,37 @@ describe("OAuth2Admin", function () {
});
await capturePage('secret_not_shown_again');
});

it('should warn when an existing client scope is reduced', async function () {
// Create a client with admin scope, then reduce it to read on edit.
await page.goto(createUrl);
await fillClientForm('Scope narrowing client', 'Confidential', 'https://narrow.example/callback', 'Matomo admin level access');
await submitForm(true);

await page.goto(adminUrl);
await editFirstClient();
await selectValue(page, 'div[name="scopes"]', 'Matomo read level access');
await submitForm();

await page.waitForSelector('#notificationContainer .notification-warning', { visible: true });
const warningText = await page.$eval('#notificationContainer .notification-warning', (el) => el.innerText);

expect(warningText).to.contain('reduced the allowed scope');
});

it('should not warn when an existing client scope is not reduced', async function () {
// Editing without lowering the scope must not raise the warning.
await page.goto(createUrl);
await fillClientForm('Scope unchanged client', 'Confidential', 'https://unchanged.example/callback', 'Matomo read level access');
await submitForm(true);

await page.goto(adminUrl);
await editFirstClient();
await page.evaluate(function () {
$('#name').val('Scope unchanged client renamed').change();
});
await submitForm();

expect(await page.$('#notificationContainer .notification-warning')).to.be.null;
});
});
2 changes: 1 addition & 1 deletion vue/dist/OAuth2.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading